package com.niiwoo.civet.trade.service.dubbo.loan;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.dubbo.rpc.RpcContext;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.dangdang.ddframe.rdb.sharding.api.HintManager;
import com.niiwoo.activity.event.enums.FlowProjectTypeEventEnum;
import com.niiwoo.activity.stage.dto.request.inviteBuddy.ProjectPublishEventDTO;
import com.niiwoo.activity.stage.dto.request.prize.ActivityPrizeResetStatusReqDTO;
import com.niiwoo.activity.stage.dto.request.prize.ActivityPrizeStatusReqDTO;
import com.niiwoo.activity.stage.enums.prize.UserPrizeSuitProjectTypeEnum;
import com.niiwoo.activity.stage.service.inviterBuddy.InviteBuddyActivityDubboService;
import com.niiwoo.activity.stage.service.prize.ActivityPrizeDubboService;
import com.niiwoo.asset.dto.mq.R360OrderStatusChangedDTO;
import com.niiwoo.asset.enums.r360.R360OrderProjectSubStatusEnum;
import com.niiwoo.asset.service.manager.R360LoanCommonDubboService;
import com.niiwoo.civet.account.dto.common.BankCardDTO;
import com.niiwoo.civet.account.dto.response.AccountBaseResponse;
import com.niiwoo.civet.account.dto.response.GetSimpleUserBankCardResponse;
import com.niiwoo.civet.account.service.AccountDubboService;
import com.niiwoo.civet.account.service.bankcard.BankCardDubboService;
import com.niiwoo.civet.base.constant.OrgConstant;
import com.niiwoo.civet.base.enums.DeadlineUnitEnum;
import com.niiwoo.civet.snapdata.dto.request.ProjectRiskInfoSaveByDetailJSONDTO;
import com.niiwoo.civet.snapdata.enums.RiskTypeEnum;
import com.niiwoo.civet.snapdata.service.ProjectRiskInfoDubboService;
import com.niiwoo.civet.snapdata.service.TianChengDataAnalysisDubboService;
import com.niiwoo.civet.system.dto.common.SysConfParamDTO;
import com.niiwoo.civet.system.enums.SysParamKeyEnum;
import com.niiwoo.civet.system.service.SysParamService;
import com.niiwoo.civet.trade.constant.RabbitConstant;
import com.niiwoo.civet.trade.dao.entity.*;
import com.niiwoo.civet.trade.dto.BorrowPrizeOccupyDTO;
import com.niiwoo.civet.trade.dto.common.ProjectDTO;
import com.niiwoo.civet.trade.dto.request.*;
import com.niiwoo.civet.trade.dto.request.databackflow.LoanApplyDataBackFlowRequestDTO;
import com.niiwoo.civet.trade.dto.request.databackflow.ProjectStatusChangeBackFlowRequestDTO;
import com.niiwoo.civet.trade.dto.request.loan.*;
import com.niiwoo.civet.trade.dto.response.*;
import com.niiwoo.civet.trade.dto.response.loan.ConfirmResponseDTO;
import com.niiwoo.civet.trade.dto.response.loan.ContractPreviewInfoDTO;
import com.niiwoo.civet.trade.dto.response.loan.FadadaWithholdContractSignResponseDTO;
import com.niiwoo.civet.trade.dto.response.loan.LoanLimitResponseDTO;
import com.niiwoo.civet.trade.dto.response.statisticsinfo.FastBorrowAccreditCityResponseDTO;
import com.niiwoo.civet.trade.enums.*;
import com.niiwoo.civet.trade.service.databackflow.TianChengDataBackFlowDubboService;
import com.niiwoo.civet.trade.service.dubbo.loan.core.ISpeedLoanFactory;
import com.niiwoo.civet.trade.service.dubbo.loan.core.ISpeedLoanService;
import com.niiwoo.civet.trade.service.loan.SpeedLoanDubboService;
import com.niiwoo.civet.trade.service.local.activity.ActivityEventService;
import com.niiwoo.civet.trade.service.local.common.ProjectCommonService;
import com.niiwoo.civet.trade.service.local.configure.TradeConfigLocalService;
import com.niiwoo.civet.trade.service.local.invest.InvestService;
import com.niiwoo.civet.trade.service.local.loan.LoanConfirmService;
import com.niiwoo.civet.trade.service.local.loan.SpeedLoanLimitService;
import com.niiwoo.civet.trade.service.local.loan.SpeedLoanRateConfigService;
import com.niiwoo.civet.trade.service.local.loan.SpeedLoanService;
import com.niiwoo.civet.trade.service.local.project.ProjectLocalService;
import com.niiwoo.civet.trade.service.project.ProjectDubboService;
import com.niiwoo.civet.user.dto.UserPersonalDubboInfoDTO;
import com.niiwoo.civet.user.dto.UserProfessionDubboInfoDTO;
import com.niiwoo.civet.user.dto.request.AddUserLoanLimitRequestDTO;
import com.niiwoo.civet.user.dto.request.DataStatusRequestDTO;
import com.niiwoo.civet.user.dto.request.SimpleOrgInfo;
import com.niiwoo.civet.user.dto.response.*;
import com.niiwoo.civet.user.enums.*;
import com.niiwoo.civet.user.service.*;
import com.niiwoo.tripod.base.component.PrivacyMasks;
import com.niiwoo.tripod.base.utils.DateUtils;
import com.niiwoo.tripod.base.utils.IdCardUtils;
import com.niiwoo.tripod.lanmao.component.LanMaoDirectService;
import com.niiwoo.tripod.lanmao.component.LanMaoSequence;
import com.niiwoo.tripod.lanmao.enums.ProjectTypeEnum;
import com.niiwoo.tripod.lanmao.enums.UserRoleEnum;
import com.niiwoo.tripod.lanmao.request.EstablishProjectRequest;
import com.niiwoo.tripod.provider.exception.BizException;
import com.niiwoo.tripod.service.component.SnowflakeIdWorker;
import com.niiwoo.tripod.tiancheng.component.TianchengAgainstFakeService;
import com.niiwoo.tripod.tiancheng.component.TianchengDataFlowService;
import com.niiwoo.tripod.tiancheng.enums.DegreeEnum;
import com.niiwoo.tripod.tiancheng.enums.FunctionCodeEnum;
import com.niiwoo.tripod.tiancheng.enums.IsNeedDetailEnum;
import com.niiwoo.tripod.tiancheng.enums.PlatformTypeEnum;
import com.niiwoo.tripod.tiancheng.request.afp.PublishLoanAgainsFakeRequest;
import com.niiwoo.tripod.tiancheng.request.afp.RiskRuleRequest;
import com.niiwoo.tripod.tiancheng.request.afp.SpeedLoanCreditAuditRequest;
import com.niiwoo.tripod.tiancheng.request.common.RelationVerification;
import com.niiwoo.tripod.tiancheng.request.df.AddressListRequest;
import com.niiwoo.tripod.tiancheng.response.common.AgainstFakeResponse;
import com.niiwoo.tripod.tiancheng.response.common.RiskRuleResponse;
import com.niiwoo.tripod.tiancheng.response.common.TianchengUapResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import org.joda.time.Days;
import org.joda.time.Duration;
import org.joda.time.LocalDate;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * @Description:
 * @Author:LiJian
 * @Date:2017/11/8
 */
@Slf4j
@Service(version = "1.0.0")
public class SpeedLoanDubboServiceImpl implements SpeedLoanDubboService {
    @Autowired
    private ApplicationContext applicationContext;
    // 天秤授信反欺诈回调地址模型(AFP)
    public static final String TIANCHENG_AFP_CALLBACK_URL = "%s/tc/%s/%s/callback?functionCode=%s";
    public static final String LOAN_QUANTITY_LIMIT_KEY = "loan_quantity_limit";
    @Value(value = "${web-http-url}")
    private String webHttpUrl;

    static final int PRECISION_TWO = 2;

    @Autowired
    private SpeedLoanService loanLocalService;
    @Autowired
    private ProjectCommonService projectCommonService;
    @Autowired
    private TradeConfigLocalService tradeConfigService;
    @Reference(version = "1.0.0")
    private UserForbidDubboService userForbidDubboService;
    @Reference(version = "1.0.0")
    private UserDataDubboService userDataDubboService;
    @Reference(version = "1.0.0")
    private UserExtInfoDubboService userExtInfoDubboService;
    @Reference(version = "1.0.0")
    private UserIdentityDubboService userIdentityDubboService;
    @Reference(version = "1.0.0")
    private UserBasicInfoDubboService userBasicInfoDubboService;
    @Reference(version = "1.0.0")
    private UserCreditDubboService userCreditService;
    @Reference(version = "1.0.0")
    private UserAuthorizationInfoDubboService userAuthorizationInfoDubboService;
    @Autowired
    private TianchengAgainstFakeService tianchengAgainstFakeService;
    @Reference(version = "1.0.0")
    private PortraitAuthDubboService portraitAuthDubboService;
    @Reference(version = "1.0.0")
    private ProjectRiskInfoDubboService projectRiskInfoDubboService;
    @Autowired
    private LanMaoDirectService lanMaoDirectService;
    @Reference(version = "1.0.0")
    private BankCardDubboService bankCardDubboService;
    @Reference(version = "1.0.0")
    private SysParamService sysParamService;
    @Reference(version = "1.0.0")
    private TianChengDataBackFlowDubboService tianChengDataBackFlowDubboService;
    @Reference(version = "1.0.0")
    private ActivityPrizeDubboService activityPrizeDubboService;
    @Reference(version = "1.0.0")
    private UserStatusDubboService userStatusDubboService;
    @Reference(version = "1.0.0")
    private InviteBuddyActivityDubboService inviteBuddyActivityDubboService;
    @Reference(version = "1.0.0")
    private R360LoanCommonDubboService r360LoanCommonDubboService;
    @Reference(version = "1.0.0")
    private OrganizationDubboService organizationDubboService;
    @Reference(version = "1.0.0")
    private UserLetterDubboService userLetterDubboService;
    @Autowired
    private InvestService investService;
    @Autowired
    PrivacyMasks privacyMasks;
    @Autowired
    private SnowflakeIdWorker snowflakeIdWorker;
    @Autowired
    TianchengDataFlowService tianchengDataFlowService;
    @Autowired
    private LanMaoSequence lanMaoSequence;
//    @Reference
//    ActivityPrizeDubboService activityPrizeDubboService;
    @Autowired
    private SpeedLoanLimitService speedLoanLimitLocalService;
    @Reference(version = "1.0.0")
    AccountDubboService accountDubboService;
//    @Reference(version = "1.0.0")
//    AuditWorkTaskService auditWorkTaskService;
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private LoanConfirmService loanConfirmService;
    @Autowired
    private SpeedLoanRateConfigService speedLoanRateConfigService;
    @Autowired
    private TradeConfigLocalService configLocalService;
    @Reference(version = "1.0.0")
    private TianChengDataAnalysisDubboService tianChengDataAnalysisDubboService;
    @Reference(version = "1.0.0")
    private SendSmsDubboService sendSmsDubboService;
    @Autowired
    private ProjectDubboService projectDubboService;
    @Autowired
    RedisTemplate<String, String> redisTemplate;
    @Autowired
    private ActivityEventService activityEventService;
    @Autowired
    private ISpeedLoanFactory iSpeedLoanFactory;

    @Autowired
    private ProjectLocalService projectLocalService;

    @Override
    public SpeedLoanUserDataStatusDTO getSpeedLoanUserDataStatus(String userId) {
        return this.getSpeedLoanDataStatus(userId);
    }

    @Override
    public SpeedLoanCreditResultDTO getSpeedLoanCreditResult(String userId) {
        SpeedLoanCreditResultDTO speedLoanCreditResult = new SpeedLoanCreditResultDTO();
        // 获取用户额度信息
        UserSpeedCreditInfoDTO userSpeedCreditInfo = userCreditService.getUserSpeedCreditInfo(userId);
        BigDecimal creditAmount = userSpeedCreditInfo.getCreditTotalAmount();
        creditAmount = (creditAmount != null ? creditAmount : BigDecimal.ZERO);
        speedLoanCreditResult.setCreditStatus(userSpeedCreditInfo.getCreditStatus());
        speedLoanCreditResult.setCreditTotalAmount(creditAmount);
        // 授信超时时间
        String creditOvertime = configLocalService.selectTradeConfigByEmun(TradeConfigEnum.CREDITOVERTIME);
        speedLoanCreditResult.setCreditOvertime(Integer.valueOf(creditOvertime));

        // 计算可用额度
        BigDecimal usedAmount = loanLocalService.getSpeedUsedCredit(userId); // 已使用额度
        speedLoanCreditResult.setCreditAviAmount(creditAmount.subtract(usedAmount));

        SpeedLoanUserDataStatusDTO userDataStatusDTO = this.getSpeedLoanDataStatus(userId);
        speedLoanCreditResult.setPersonalInfoStatus(userDataStatusDTO.getPersonalInfoStatus());
        speedLoanCreditResult.setIdentityInfoStatus(userDataStatusDTO.getIdentityInfoStatus());
        speedLoanCreditResult.setBankCardStatus(userDataStatusDTO.getBankCardStatus());
        speedLoanCreditResult.setAuthStatus(userDataStatusDTO.getAuthStatus());
        speedLoanCreditResult.setZhimaAuthStatus(userDataStatusDTO.getZhimaAuthStatus());
        speedLoanCreditResult.setTelOperatorAuthStatus(userDataStatusDTO.getTelOperatorAuthStatus());
        speedLoanCreditResult.setSocialSecurityStatus(userDataStatusDTO.getSocialSecurityStatus());
        speedLoanCreditResult.setCreditCardStatus(userDataStatusDTO.getCreditCardStatus());
        speedLoanCreditResult.setSocialSecurityOrCreditCard(userDataStatusDTO.getSocialSecurityOrCreditCard());

        return speedLoanCreditResult;
    }

    /**
     * 组装极速借资料状态
     * @param userId
     * @return
     */
    private SpeedLoanUserDataStatusDTO getSpeedLoanDataStatus(String userId) {
        SpeedLoanUserDataStatusDTO loanUserDataStatusDTO = new SpeedLoanUserDataStatusDTO();
        // 获取芝麻和社保开关配置
        List<TradeConfigEnum> enumList = new ArrayList<>();
        enumList.add(TradeConfigEnum.SESAMEAUTHORIZATIONSTATUS);
        enumList.add(TradeConfigEnum.SOCIALSECURITYAUTHORIZATIONSTATUS);
        enumList.add(TradeConfigEnum.SOCIALSECURITYORCREDITCARD);
        Map<String, String> configMap = configLocalService.selectTradeConfigByEmuns(enumList);
        Byte zhimaSwitchStatus = Byte.valueOf(configMap.get(TradeConfigEnum.SESAMEAUTHORIZATIONSTATUS.getConfigKey()));
        Byte socialSecuritySwitchStatus = Byte.valueOf(configMap.get(TradeConfigEnum.SOCIALSECURITYAUTHORIZATIONSTATUS.getConfigKey()));
        Byte socialSecurityOrCreditCard = Byte.valueOf(configMap.get(TradeConfigEnum.SOCIALSECURITYORCREDITCARD.getConfigKey()));
        loanUserDataStatusDTO.setZhimaSwitchStatus(zhimaSwitchStatus);
        loanUserDataStatusDTO.setSocialSecuritySwitchStatus(socialSecuritySwitchStatus);
        loanUserDataStatusDTO.setSocialSecurityOrCreditCard(socialSecurityOrCreditCard);

        // 数据库状态处理
        UserCreditDataStatusDTO userCreditDataStatus = userCreditService.getUserCreditDataStatusDTO(userId);
        loanUserDataStatusDTO.setPersonalInfoStatus(userCreditDataStatus.getPersonInfoStatus());
        loanUserDataStatusDTO.setBankCardStatus(userCreditDataStatus.getOpenBankAccountStatus());

        Byte zhimaStatus = userCreditDataStatus.getZhimaCreditStatus();
        loanUserDataStatusDTO.setZhimaAuthStatus(zhimaStatus);
        // 芝麻授信中、过期、和授信失败统一给前端是未授信
        if(UserDataStatusEnum.ZhimaCreditStatus.APPLYING.getStatus().equals(zhimaStatus) || UserDataStatusEnum.ZhimaCreditStatus.FAIL_APPLY.getStatus().equals(zhimaStatus)
                || UserDataStatusEnum.ZhimaCreditStatus.EXPIRED.getStatus().equals(zhimaStatus)) {
            loanUserDataStatusDTO.setZhimaAuthStatus((byte) 0);
        }else if(UserDataStatusEnum.ZhimaCreditStatus.PASS_APPLY.getStatus().equals(zhimaStatus)) {
            loanUserDataStatusDTO.setZhimaAuthStatus((byte) 1);
        }
        loanUserDataStatusDTO.setTelOperatorAuthStatus(userCreditDataStatus.getPhoneOperatorStatus());
        loanUserDataStatusDTO.setSocialSecurityStatus(userCreditDataStatus.getSocialSecurityStatus());
        // 身份认证状态
        loanUserDataStatusDTO.setIdentityInfoStatus((byte) 0);
        // 关闭OCR，判断手持照是否上传，如果上传则说明身份信息完成
        if(userCreditDataStatus.getOCRScanSwitch().byteValue() == 0 &&
                userCreditDataStatus.getImgUploadStatus() == UserDataStatusEnum.HandPhotoStatus.UPLOADED.getStatus().byteValue()) {
            loanUserDataStatusDTO.setIdentityInfoStatus((byte) 1);
        }
        // 已身份证OCR、身份证未过期且离过期日期超过30天、肖像认证成功或信审后台标识为已通过
        if(userCreditDataStatus.getOCRStatus().equals(UserDataStatusEnum.OCRStatus.YES_OCR.getStatus()) &&
                userCreditDataStatus.getRealNameAuthStatus().equals(UserDataStatusEnum.RealNameAuthStatus.PASS_CERTIFIED.getStatus())
                && (userCreditDataStatus.getPortraitAuthStatus().equals(UserDataStatusEnum.PortraitAuthStatus.PASS_CERTIFIED.getStatus())
                || userCreditDataStatus.getPortraitAuthStatus().equals(UserDataStatusEnum.PortraitAuthStatus.NEED_APPROVE.getStatus()))
                && userCreditDataStatus.getIdCardStatus().equals(UserDataStatusEnum.IdCardExpiredStatus.VALID.getStatus())) {
            loanUserDataStatusDTO.setIdentityInfoStatus((byte) 1);
        }
        // 提交成功（OCR已开启情况下）
        if(userCreditDataStatus.getOCRScanSwitch().byteValue() == 1 && userCreditDataStatus.getOCRStatus().equals(UserDataStatusEnum.OCRStatus.YES_OCR.getStatus())
                && userCreditDataStatus.getPortraitAuthStatus().equals(UserDataStatusEnum.PortraitAuthStatus.NEED_APPROVE.getStatus())) {
            loanUserDataStatusDTO.setIdentityInfoStatus((byte) 4);
        }
        // 已过期（OCR已开启情况下）
        if(userCreditDataStatus.getOCRScanSwitch().byteValue() == 1 && userCreditDataStatus.getIdCardStatus() == UserDataStatusEnum.IdCardExpiredStatus.EXPIRED.getStatus().byteValue()) {
            loanUserDataStatusDTO.setIdentityInfoStatus((byte) 3);
        }
        // 快过期（OCR已开启情况下）
        if(userCreditDataStatus.getOCRScanSwitch().byteValue() == 1 && userCreditDataStatus.getIdCardStatus() == UserDataStatusEnum.IdCardExpiredStatus.EXPIRING.getStatus().byteValue()) {
            loanUserDataStatusDTO.setIdentityInfoStatus((byte) 2);
        }

        // 运营商转换
        Byte phoneOperatorStatus = userCreditDataStatus.getPhoneOperatorStatus();
        if(UserDataStatusEnum.PhoneOperatorStatus.GETFAIL.getStatus().equals(phoneOperatorStatus) ) {
            loanUserDataStatusDTO.setTelOperatorAuthStatus((byte) 0);
        }
        if(UserDataStatusEnum.PhoneOperatorStatus.EXPIRED.getStatus().equals(phoneOperatorStatus) ) {
            loanUserDataStatusDTO.setTelOperatorAuthStatus((byte) 3);
        }

        // 社保状态转换
        Byte socialSecurityStatus = userCreditDataStatus.getSocialSecurityStatus();
        loanUserDataStatusDTO.setSocialSecurityStatus(socialSecurityStatus);
        if(UserDataStatusEnum.SocialSecurityStatus.GETFAIL.getStatus().equals(socialSecurityStatus)
              ||UserDataStatusEnum.SocialSecurityStatus.DIFFER.getStatus().equals(socialSecurityStatus)) {
            loanUserDataStatusDTO.setSocialSecurityStatus((byte) 0);
        }
        if(UserDataStatusEnum.SocialSecurityStatus.EXPIRED.getStatus().equals(socialSecurityStatus)) {
            loanUserDataStatusDTO.setSocialSecurityStatus((byte) 3);
        }

        // 授权状态
        loanUserDataStatusDTO.setAuthStatus((byte) 0);
        if(UserDataStatusEnum.PhoneOperatorStatus.ALREADY_CREDIT.getStatus().equals(phoneOperatorStatus) || UserDataStatusEnum.PhoneOperatorStatus.GETSUCCESS.getStatus().equals(phoneOperatorStatus)) {
            if(UserDataStatusEnum.ZhimaCreditStatus.PASS_APPLY.getStatus().equals(zhimaStatus) || zhimaSwitchStatus == 0) {
                loanUserDataStatusDTO.setAuthStatus((byte) 1); // 已完成（成功授权运营商、已成功授权芝麻或芝麻关闭）
            }
        }
        if(UserDataStatusEnum.PhoneOperatorStatus.EXPIRED.getStatus().equals(phoneOperatorStatus)) {
            loanUserDataStatusDTO.setAuthStatus((byte) 2); // 已过期（运营商已过期）
        }

        // 信用卡状态转换
        byte creditCardStatus = userCreditDataStatus.getCreditCardStatus().byteValue();
        loanUserDataStatusDTO.setCreditCardStatus(creditCardStatus);
        if(UserDataStatusEnum.CreditCardStatus.GETFAIL.getStatus().byteValue()==creditCardStatus) {
            loanUserDataStatusDTO.setCreditCardStatus((byte) 0);
        }
        if(UserDataStatusEnum.CreditCardStatus.EXPIRED.getStatus().byteValue()==creditCardStatus) {
            loanUserDataStatusDTO.setCreditCardStatus((byte) 3);
        }
        log.info("getSpeedLoanDataStatus.userId={},loanUserDataStatusDTO={}",userId,JSON.toJSONString(loanUserDataStatusDTO));
        byte finalAuthStatus = this.getFinalAuthStatus(loanUserDataStatusDTO);
        log.info("getSpeedLoanDataStatus.userId={},finalAuthStatus={}",userId,finalAuthStatus);
        loanUserDataStatusDTO.setAuthStatus(finalAuthStatus);
        return loanUserDataStatusDTO;
    }

    private byte getFinalAuthStatus(SpeedLoanUserDataStatusDTO userDataStatusDTO){
        byte authStatus = userDataStatusDTO.getAuthStatus();
        byte socialSecurityAndCreditCardStatus = this.getSocialSecurityAndCreditCardStatus(userDataStatusDTO);
        log.info("getFinalAuthStatus.socialSecurityAndCreditCardStatus={}",socialSecurityAndCreditCardStatus);
        if((byte) 1==authStatus && (byte) 1==socialSecurityAndCreditCardStatus){
            return (byte) 1;
        }

        if((byte) 2==authStatus || (byte) 3==socialSecurityAndCreditCardStatus){
            return (byte) 2;
        }

        return (byte) 0;
    }

    //获取社保和信用卡组合状态
    private byte getSocialSecurityAndCreditCardStatus(SpeedLoanUserDataStatusDTO userDataStatusDTO){
        byte socialSecurityOrCreditCard = userDataStatusDTO.getSocialSecurityOrCreditCard();//社保、信用卡二选一开关(1：开启   0：关闭)
        if(SocialSecurityCreditCardSwitchEnum.CLOSE.getValue().byteValue()==socialSecurityOrCreditCard){
            return (byte) 1; //如果开关为关闭，直接返回1：授权成功
        }

        byte socialSecurityStatus = this.getSocialSecurityStatus(userDataStatusDTO);//(0、未完成 1、已授权 2、已完成 3、已过期)
        byte creditCardStatus = userDataStatusDTO.getCreditCardStatus();//信用卡状态 0、未认证 1、已认证 2、已验证 3、已过期
        if((byte) 1==socialSecurityStatus || (byte) 1==creditCardStatus || (byte) 2==creditCardStatus){ //其中一个为“已授权”，则返回“已授权”
            return (byte) 1;
        }

        if( ((byte) 0==socialSecurityStatus || (byte) 5==socialSecurityStatus)
                && (byte) 0==creditCardStatus){
            return (byte) 0; //信用卡“未完成”并且社保（“为完成”或社保开关关闭），则返回“未完成”
        }

        return (byte) 3;
    }

    //获取社保授权状态
    private byte getSocialSecurityStatus(SpeedLoanUserDataStatusDTO userDataStatusDTO){
        byte socialSecuritySwitchStatus = userDataStatusDTO.getSocialSecuritySwitchStatus().byteValue();//社保开关状态（0、关闭 1、打开）
        if(SocialSecuritySwitchEnum.CLOSE.getValue().byteValue()==socialSecuritySwitchStatus){
            return (byte) 5; //表示无需判断社保状态
        }
        byte socialSecurityStatus = userDataStatusDTO.getSocialSecurityStatus().byteValue();
        if((byte) 1==socialSecurityStatus || (byte) 2==socialSecurityStatus){
            return (byte) 1;
        }
        log.info("getSocialSecurityStatus.socialSecurityStatus={}",socialSecurityStatus);
        return socialSecurityStatus;//(0、未完成 1、已授权 2、已完成 3、已过期)
    }

    private String createForbidTip(Date Time) {
        FastDateFormat fdf = FastDateFormat.getInstance("yyyy年MM月dd日");
        StringBuilder tipSb = new StringBuilder();
        tipSb.append("抱歉，您的综合信用评分不足，暂时无法借钱，请于");
        tipSb.append(fdf.format(Time));
        tipSb.append("后再来申请！\n\n如有疑问，请联系钱小二！");

        return tipSb.toString();
    }

    /**
     * 检查是否允许发布借款
     *
     * @param request
     * @return
     */
    @Override
    public SpeedLoanApplyCommonResponseDTO speedLoanPublishCheck(SpeedLoanPublishCheckRequestDTO request) {
        log.info("SpeedLoanDubboServiceImpl.speedLoanPublishCheck.request={}",JSON.toJSONString(request));
        SpeedLoanApplyCommonResponseDTO resposeDTO = new SpeedLoanApplyCommonResponseDTO();
        resposeDTO.setStatus(LoanLimitMessageEnum.LIMIT_10099.getLimitCode());
        try {
            request.setProjectApplyCheckTypeEnum(ProjectApplyCheckTypeEnum.NO_SUBMIT_CHECK);
            this.speedLoanPublishCheckMain(request, resposeDTO);
            resposeDTO.setStatus(LoanLimitMessageEnum.LIMIT_10000.getLimitCode());
        } catch (BizException bizException) {
            log.error("speedLoanPublishCheck error",bizException);
            this.backProjectDetailUrlDeal(bizException,request,resposeDTO);
        }
        log.info("SpeedLoanDubboServiceImpl.speedLoanPublishCheck.resposeDTO={}",JSON.toJSONString(resposeDTO));
        return resposeDTO;
    }

    //退回标的项目详情跳转URL处理
    private void backProjectDetailUrlDeal(BizException bizException,SpeedLoanPublishCheckRequestDTO request,SpeedLoanApplyCommonResponseDTO resposeDTO){
        try {
            if(resposeDTO==null ||  LoanLimitMessageEnum.LIMIT_10012.getLimitCode()!=resposeDTO.getStatus()){
                return;
            }
            SysConfParamDTO sysConfParamDto = sysParamService.queryParamByKey(SysParamKeyEnum.HTML5_APP_URL_LOAN_MYLOANDETAIL.getKey());
            if(sysConfParamDto==null){
                log.error("backProjectDetailUrlDeal sysConfParamDto is null");
                return;
            }
            Map<String, Object> extendData = resposeDTO.getExtendData();
            if(extendData==null){
                extendData = new HashMap<>();
            }
            extendData.put("detailUrl",sysConfParamDto.getParamValue());
            resposeDTO.setExtendData(extendData);
        } catch (Exception e) {
            log.error("backProjectDetailUrlDeal error request={}",JSON.toJSONString(request),e);
        }
    }

    /**
     * 检查是否允许发布借款
     *
     * @param requestDTO
     * @param resposeDTO
     */
    public void speedLoanPublishCheckMain(SpeedLoanPublishCheckRequestDTO requestDTO, SpeedLoanApplyCommonResponseDTO resposeDTO) {
        this.speedLoanPublishCheck_fromUserData(requestDTO, resposeDTO);
        this.speedLoanPublishCheck_fromProjectData(requestDTO, resposeDTO);
    }

    /**
     * 检查是否允许发布借款-通过远程数据
     *
     * @param requestDTO
     * @param resposeDTO
     */
    private void speedLoanPublishCheck_fromUserData(SpeedLoanPublishCheckRequestDTO requestDTO, SpeedLoanApplyCommonResponseDTO resposeDTO) {
        //用户信息检查
        this.userInfoCheck(requestDTO, resposeDTO);
        //存在进件中融360标的检查
        this.isExsitRong360UserPushLoanDataCheck(requestDTO, resposeDTO);
        //黑名单检查
        this.speedLoanPublishCheck_blackListCheck(requestDTO, resposeDTO);
        //行为限制检查
        this.speedLoanPublishCheck_forbidCheck(requestDTO, resposeDTO);
        //天秤借款限制检查
        this.speedLoanPublishCheck_libraLoanLimitCheck(requestDTO, resposeDTO);
        //用户地址检查
        this.speedLoanPublishCheck_userCityCheck(requestDTO, resposeDTO);
        //剩余额度检查
        this.speedLoanPublishCheck_aviAmountCheck(requestDTO, resposeDTO);
    }

    /**
     * 检查是否允许发布借款-通过本地数据
     *
     * @param request
     * @param resposeDTO
     */
    private void speedLoanPublishCheck_fromProjectData(SpeedLoanPublishCheckRequestDTO request, SpeedLoanApplyCommonResponseDTO resposeDTO) {
        loanLocalService.speedLoanPublishCheck_projectCheck(request, resposeDTO);
    }

    //您在融360的借款申请正在处理中，了解详情请联系钱小二
    private void isExsitRong360UserPushLoanDataCheck(SpeedLoanPublishCheckRequestDTO request, SpeedLoanApplyCommonResponseDTO resposeDTO) {
        String userId = request.getUserId();
        if(ProjectFromSourceEnum.R360.getType().byteValue()==request.getFromSource().byteValue()){ //如果来源是融360，则不需要检查
            log.info("isExsitRong360UserPushLoanDataCheck.userId={},融360标的提交，无需检查是否存在进件中融360标的",userId);
            return;
        }
        UserBasicInfoResponseDTO userBasicInfo = userBasicInfoDubboService.getUserBasicInfo(userId);
        boolean isExsitRong360UserPushLoanData = r360LoanCommonDubboService.isExsitRong360UserPushLoanData(userBasicInfo.getMobileNo());

        log.info("isExsitRong360UserPushLoanDataCheck.userId={},isExsitRong360UserPushLoanData={}",userId,isExsitRong360UserPushLoanData);
        if(isExsitRong360UserPushLoanData) {
            log.error("isExsitRong360UserPushLoanDataCheck.userId={},存在进件中融360标的",userId);
            this.speedLoanPublishCheck_excetipnResponseDeal(LoanLimitMessageEnum.LIMIT_20001, resposeDTO);//存在进件中融360标的
        }
    }

    /**
     * 用户信息检查
     *
     * @param request
     * @param resposeDTO
     */
    private void userInfoCheck(SpeedLoanPublishCheckRequestDTO request, SpeedLoanApplyCommonResponseDTO resposeDTO) {
        //用户基本信息检查
        this.userInfoCheck_basicInfoCheck(request,resposeDTO);
        //年龄限制检查
        this.userInfoCheck_userAgeLimitCheck(request.getUserId(),resposeDTO);
    }

    private void userInfoCheck_basicInfoCheck(SpeedLoanPublishCheckRequestDTO request, SpeedLoanApplyCommonResponseDTO resposeDTO){
        if(ProjectApplyCheckTypeEnum.NO_SUBMIT_CHECK.getCheckType()==request.getProjectApplyCheckTypeEnum().getCheckType()){
            return;
        }

        //用户信息检查（基本信息、个人信息、职业信息、账户）
        UserInfoCheckResposeDTO userInfoCheckResposeDTO = userDataDubboService.userInfoCheck(request.getUserId());
        log.info("SpeedLoanDubboServiceImpl.userInfoCheck.userInfoCheckResposeDTO={}",JSON.toJSONString(userInfoCheckResposeDTO));
        int checkStatus = userInfoCheckResposeDTO.getCheckStatus();
        if ( LoanLimitMessageEnum.LIMIT_10000.getLimitCode()!=checkStatus) {
            resposeDTO.setStatus(checkStatus);
            resposeDTO.setAlertTitle(userInfoCheckResposeDTO.getAlertTitle());
            resposeDTO.setAlertDesc(userInfoCheckResposeDTO.getAlertDesc());
            throw new BizException("TRD10006");
        }
    }

    private void userInfoCheck_userAgeLimitCheck(String userId,SpeedLoanApplyCommonResponseDTO resposeDTO) {
        log.info("userInfoCheck_userAgeLimitCheck.userId={} start...",userId);
        //默认都为22-55
        int minAge = 22;
        int maxAge = 55;
        Map<String, String> ageLimitConfigMap = null;
        try {
            ageLimitConfigMap = tradeConfigService.selectTradeConfigByEmuns(TradeConfigEnum.MINACCESSAGE,TradeConfigEnum.MAXACCESSAGE);
            log.info("userInfoCheck_userAgeLimitCheck.userId={},ageLimitConfigMap={}",userId,JSON.toJSONString(ageLimitConfigMap));
            minAge = Integer.parseInt(ageLimitConfigMap.get(TradeConfigEnum.MINACCESSAGE.getConfigKey()));
            maxAge = Integer.parseInt(ageLimitConfigMap.get(TradeConfigEnum.MAXACCESSAGE.getConfigKey()));
        } catch (NumberFormatException e) {
            log.error("userInfoCheck_userAgeLimitCheck.userId={},获取借款年龄配置错误",userId,e);
        }

        UserIdentityInfoResponseDTO userIdentityInfoResponseDTO = userIdentityDubboService.get(userId);
        log.info("userInfoCheck_userAgeLimitCheck.userId={},userIdentityInfoResponseDTO={}",userId,JSON.toJSONString(userIdentityInfoResponseDTO));
        if(userIdentityInfoResponseDTO==null){
            log.info("userInfoCheck_userAgeLimitCheck.userId={},用户身份信息为空，不做校验",userId);
            return;
        }
        String idCard = userIdentityInfoResponseDTO.getIdCard();
        int age = IdCardUtils.getAgeByIdCard(idCard); //根据身份证号获取年龄
        log.info("userInfoCheck_userAgeLimitCheck.userId={},age={}",userId,age);
        if(age<minAge || age>maxAge){
            String alertTitle=String.format(LoanLimitMessageEnum.LIMIT_10027.getLimitMessageTitle(), "借款");
            String alertDesc=String.format(LoanLimitMessageEnum.LIMIT_10027.getLimitMessageDesc(), "借款",minAge,maxAge);
            resposeDTO.setStatus(LoanLimitMessageEnum.LIMIT_10027.getLimitCode());
            resposeDTO.setAlertTitle(alertTitle);
            resposeDTO.setAlertDesc(alertDesc);
            throw new BizException("TRD10006");
        }
        log.info("userInfoCheck_userAgeLimitCheck.userId={} end...",userId);
    }

    //黑名单检查
    public void speedLoanPublishCheck_blackListCheck(SpeedLoanPublishCheckRequestDTO requestDTO, SpeedLoanApplyCommonResponseDTO resposeDTO) {
        String userId = requestDTO.getUserId();
        UserBlackListInfoDTO blackListInfoDTO = userForbidDubboService.getUserBlackListInfo(userId);
        if (blackListInfoDTO == null) {
            return;
        }
        resposeDTO.setStatus(LoanLimitMessageEnum.LIMIT_10031.getLimitCode());
        resposeDTO.setAlertTitle(LoanLimitMessageEnum.LIMIT_10031.getLimitMessageTitle());
        if (Days.daysBetween(new LocalDate(),new LocalDate(blackListInfoDTO.getEndTime())).getDays() < 60) {
            resposeDTO.setAlertDesc(createForbidTip(blackListInfoDTO.getEndTime()));
        } else {
            resposeDTO.setAlertDesc("抱歉，您的综合信用评分不足，暂时无法借钱。\n\n如有疑问，请联系钱小二！");
        }
        throw new BizException("TRD10006");
    }

    //行为限制检查
    public void speedLoanPublishCheck_forbidCheck(SpeedLoanPublishCheckRequestDTO requestDTO, SpeedLoanApplyCommonResponseDTO resposeDTO) {
        String userId = requestDTO.getUserId();
        ValidUserForbidResponseDTO validUserForbidResponseDTO = userForbidDubboService.queryValidUserForbidInfo(userId, UserForbidTypeEnum.SPEEDBORROWED);
        if (validUserForbidResponseDTO != null) {
            this.speedLoanPublishCheck_excetipnResponseDeal(LoanLimitMessageEnum.LIMIT_10003, resposeDTO);//暂被限制申请借款！
        }
    }

    //天秤借款限制检查
    public void speedLoanPublishCheck_libraLoanLimitCheck(SpeedLoanPublishCheckRequestDTO requestDTO, SpeedLoanApplyCommonResponseDTO resposeDTO) {
        String userId = requestDTO.getUserId();
        UserLibraLoanLimitDTO userLibraLoanLimitDTO = userForbidDubboService.getUserLibraLoanLimit(userId, ProductTypeEnum.SPEED_LOAN);
        if (userLibraLoanLimitDTO == null) {
            return;
        }
        resposeDTO.setStatus(LoanLimitMessageEnum.LIMIT_10031.getLimitCode());
        resposeDTO.setAlertTitle(LoanLimitMessageEnum.LIMIT_10031.getLimitMessageTitle());
        if (Days.daysBetween(new LocalDate(userLibraLoanLimitDTO.getLimitedTime()), new LocalDate()).getDays() < 60) {
            resposeDTO.setAlertDesc(createForbidTip(userLibraLoanLimitDTO.getLimitedTime()));
        } else {
            resposeDTO.setAlertDesc("抱歉，您的综合信用评分不足，暂时无法借钱。\n\n如有疑问，请联系钱小二！");
        }
        throw new BizException("TRD10006");
    }

    //用户地址检查检查
    private void speedLoanPublishCheck_userCityCheck(SpeedLoanPublishCheckRequestDTO requestDTO, SpeedLoanApplyCommonResponseDTO resposeDTO) {
        String userId = requestDTO.getUserId();

        //是否在全国范围内开通 0:全国开通 1:局部开通
        String speedBorrowCountryOnOff = tradeConfigService.selectTradeConfigByEmun(TradeConfigEnum.SPEEDBORROWCOUNTRYONOFF);
        log.info("发布借款-userId:" + userId + " 开通城市检查 结果：speedBorrowCountryOnOff=" + speedBorrowCountryOnOff);
        if ("0".equals(speedBorrowCountryOnOff)) {
            log.info("发布借款-userId:" + userId + " 开通城市检查 结果：通过 原因：开通配置为全国范围");
            return;
        }

        //获取开放城市
        String speedBorrowCityCode = tradeConfigService.selectTradeConfigByEmun(TradeConfigEnum.SPEEDBORROWCITYCODE);
        log.info("发布借款-userId:" + userId + " 开通城市检查 结果：speedBorrowCityCode=" + speedBorrowCityCode);
        //未配置限制城市 则当全国开通
        if (StringUtils.isEmpty(speedBorrowCityCode)) {
            log.info("发布借款-userId:" + userId + " 开通城市检查 结果：通过 原因：开通城市未配置");
            this.speedLoanPublishCheck_excetipnResponseDeal(LoanLimitMessageEnum.LIMIT_10021, resposeDTO);
        }

        try {
            //借款地检查
            this.loanPublishCityIsOpenCheck_loadAreaCheck(speedBorrowCityCode, requestDTO);
            //用户身份证所在地址检查
            this.loanPublishCityIsOpenCheck_idCardCheck(speedBorrowCityCode, userId);
            //居住地检查
            this.loanPublishCityIsOpenCheck_livingAreaCheck(speedBorrowCityCode, userId);
            //工作地地检查
            this.loanPublishCityIsOpenCheck_companyAreaCheck(speedBorrowCityCode, userId);
        } catch (BizException bizException) {
            this.speedLoanPublishCheck_excetipnResponseDeal(LoanLimitMessageEnum.LIMIT_10021, resposeDTO);
        }
    }

    //用户身份证所在地址检查
    private void loanPublishCityIsOpenCheck_idCardCheck(String speedBorrowCityCode, String userId) {
        log.info("loanPublishCityIsOpenCheck_idCardCheck.userId={},speedBorrowCityCode={} start",userId,speedBorrowCityCode);
        //获取用户身份证号
        UserIdentityInfoResponseDTO userIdentityInfoResponseDTO = userIdentityDubboService.get(userId);
        log.info("loanPublishCityIsOpenCheck_idCardCheck.userId={},speedBorrowCityCode={},userIdentityInfoResponseDTO=",userId,speedBorrowCityCode,JSON.toJSONString(userIdentityInfoResponseDTO));
        if(userIdentityInfoResponseDTO==null){
            log.info("用户未录入身份信息，不做校验");
            return;
        }
        String idCard = userIdentityInfoResponseDTO.getIdCard();
        //身份证所在城市的编码
        String cityCode = idCard.substring(0, 4) + "00";
        if (!speedBorrowCityCode.contains(cityCode)) {
            log.error("loanPublishCityIsOpenCheck_idCardCheck error userId=" + userId + ",cityCode=" + cityCode);
            throw new BizException("TRD10006");// 所在地未开放借款服务！极速借
        }
        log.info("loanPublishCityIsOpenCheck_idCardCheck.userId={},speedBorrowCityCode={} end",userId,speedBorrowCityCode);
    }

    //居住地检查
    private void loanPublishCityIsOpenCheck_livingAreaCheck(String speedBorrowCityCode, String userId) {
        log.info("loanPublishCityIsOpenCheck_livingAreaCheck.userId={},speedBorrowCityCode={} start...",userId,speedBorrowCityCode);
        //获取用户个人信息
        UserPersonalDubboInfoDTO personalInfo = userDataDubboService.userPersonalInfo(userId);
        log.info("loanPublishCityIsOpenCheck_livingAreaCheck.userId={},speedBorrowCityCode={} personalInfo={}",userId,speedBorrowCityCode,JSON.toJSONString(personalInfo));
        if(personalInfo==null){
            log.info("用户未录入个人信息，不做校验");
            return;
        }
        this.loanPublishCityIsOpenCheck(speedBorrowCityCode, personalInfo.getLivingProvince(), personalInfo.getLivingCity());
        log.info("loanPublishCityIsOpenCheck_livingAreaCheck.userId={},speedBorrowCityCode={} end...",userId,speedBorrowCityCode);
    }

    //工作地地检查
    private void loanPublishCityIsOpenCheck_companyAreaCheck(String speedBorrowCityCode, String userId) {
        log.info("loanPublishCityIsOpenCheck_companyAreaCheck.userId={},speedBorrowCityCode={} start...",userId,speedBorrowCityCode);
        //获取职业信息
        UserProfessionDubboInfoDTO professionInfo = userDataDubboService.userProfessionInfo(userId);
        log.info("loanPublishCityIsOpenCheck_companyAreaCheck.userId={},speedBorrowCityCode={},professionInfo={}",userId,speedBorrowCityCode,JSON.toJSONString(professionInfo));
        if(professionInfo==null){
            log.info("用户未录入职业信息，不做校验");
            return;
        }
        this.loanPublishCityIsOpenCheck(speedBorrowCityCode, professionInfo.getCompanyProvince(), professionInfo.getCompanyCity());
        log.info("loanPublishCityIsOpenCheck_companyAreaCheck.userId={},speedBorrowCityCode={} end...",userId,speedBorrowCityCode);
    }

    //借款地检查
    private void loanPublishCityIsOpenCheck_loadAreaCheck(String speedBorrowCityCode, SpeedLoanPublishCheckRequestDTO requestDTO) {
        String userId = requestDTO.getUserId();
        log.info("loanPublishCityIsOpenCheck_loadAreaCheck start userId={}",userId);
        this.loanPublishCityIsOpenCheck(speedBorrowCityCode, requestDTO.getProvince(), requestDTO.getCity());
        log.info("loanPublishCityIsOpenCheck_loadAreaCheck end userId={}",userId);
    }

    /**
     * 所在地申请借款是否开通检查
     *
     * @param province
     * @param city
     */
    private void loanPublishCityIsOpenCheck(String speedBorrowCityCode, String province, String city) {
        log.info("loanPublishCityIsOpenCheck province=" + province + ",city=" + city);
        String areaCode = "";
        //直辖市用省份字段判断
        if (province.contains("北京") || province.contains("天津") || province.contains("上海") || province.contains("重庆") || province.contains("香港") || province.contains("澳门")) {
            areaCode = userDataDubboService.selectAreaCodeByAreaName(province);
        } else if (city != null) {
            areaCode = userDataDubboService.selectAreaCodeByAreaName(city);
        }
        log.info("loanPublishCityIsOpenCheck areaCode=" + areaCode);
        if (!StringUtils.isEmpty(areaCode) && speedBorrowCityCode.contains(areaCode)) {
            return;
        }
        log.error("loanPublishCityIsOpenCheck error");
        throw new BizException("TRD10006");// 所在地未开放借款服务！极速借
    }

    //借款金额检查
    public void speedLoanPublishCheck_aviAmountCheck(SpeedLoanPublishCheckRequestDTO requestDTO, SpeedLoanApplyCommonResponseDTO resposeDTO) {
        String userId = requestDTO.getUserId();

        ProjectApplyCheckTypeEnum projectApplyCheckTypeEnum = requestDTO.getProjectApplyCheckTypeEnum();
        if(ProjectApplyCheckTypeEnum.NEW_PROJ_SUBMIT_CHECK.getCheckType()!=projectApplyCheckTypeEnum.getCheckType()
                && ProjectApplyCheckTypeEnum.BACK_PROJ_SUBMIT_CHECK.getCheckType()!=projectApplyCheckTypeEnum.getCheckType()){
            log.info("speedLoanPublishCheck_loanAmountCheck.userId={},非交时不需要检查额度",userId);
            return;
        }

        Map<String, String> amountLimitConfigMap = tradeConfigService.selectTradeConfigByEmuns(TradeConfigEnum.MINAMOUNT,TradeConfigEnum.MAXAMOUNT);
        log.info("speedLoanPublishCheck_loanAmountCheck.userId={},amountLimitConfigMap={}",userId,JSON.toJSONString(amountLimitConfigMap));

        //获取授信额度
        BigDecimal speedLoanCreditTotalAmount = this.getSpeedLoanCreditTotalAmount(userId);
        //获取剩余借款额度
        BigDecimal aviAmount = this.getAviAmount(userId,speedLoanCreditTotalAmount,amountLimitConfigMap);
        //获取最小借款额度
        BigDecimal minAmount = this.getMinAmount(userId,amountLimitConfigMap);

        BigDecimal loanAmount = requestDTO.getLoanAmount(); //借款额度

        if (loanAmount.compareTo(minAmount)<0 || loanAmount.compareTo(aviAmount)>0) {
            log.info("speedLoanPublishCheck_loanAmountCheck.userId={},aviAmount={},loanAmount={}",userId,aviAmount,requestDTO.getLoanAmount());
            this.speedLoanPublishCheck_excetipnResponseDeal(LoanLimitMessageEnum.LIMIT_10007, resposeDTO);
        }

        log.info("speedLoanPublishCheck_loanAmountCheck.userId={} end",userId);
    }

    /**
     * 获取极速借剩余可用借款额度
     * @param userId
     * @return
     */
    @Override
    public BigDecimal getSpeedLoanAviAmount(String userId) {
        Map<String, String> amountLimitConfigMap = tradeConfigService.selectTradeConfigByEmuns(TradeConfigEnum.MINAMOUNT, TradeConfigEnum.MAXAMOUNT);

        // 获取授信额度
        BigDecimal speedLoanCreditTotalAmount = this.getSpeedLoanCreditTotalAmount(userId);

        // 获取剩余借款额度
        BigDecimal aviAmount = this.getAviAmount(userId, speedLoanCreditTotalAmount, amountLimitConfigMap);

        return aviAmount;
    }

    private BigDecimal getAviAmount(String userId,BigDecimal speedLoanCreditTotalAmount,Map<String, String> amountLimitConfigMap){
        BigDecimal maxAmount = new BigDecimal("500000");//默认最大借款额度为500000
        if(amountLimitConfigMap!=null){
            String maxAmountStr = amountLimitConfigMap.get(TradeConfigEnum.MAXAMOUNT.getConfigKey());
            maxAmount = (StringUtils.hasText(maxAmountStr)?new BigDecimal(maxAmountStr):maxAmount);
        }
        log.info("getAviAmount.userId={},maxAmount={}",userId,maxAmount);

        //获取用户已使用额度
        BigDecimal speedUsedCredit = loanLocalService.getSpeedUsedCredit(userId);
        log.info("getAviAmount.userId={},speedUsedCredit={}",userId,speedUsedCredit);

        //剩余额度
        BigDecimal speedAviCredit = speedLoanCreditTotalAmount.subtract(speedUsedCredit);
        log.info("getAviAmount.userId={},speedAviCredit={}",userId,speedAviCredit);
        speedAviCredit=speedAviCredit.compareTo(BigDecimal.ZERO)<0?BigDecimal.ZERO:speedAviCredit;

        BigDecimal aviAmount = (speedAviCredit.compareTo(maxAmount)<0?speedAviCredit:maxAmount);
        log.info("getAviAmount.userId={},aviAmount={}",userId,aviAmount);
        return aviAmount;
    }

    private BigDecimal getMinAmount(String userId,Map<String, String> amountLimitConfigMap){
        BigDecimal minAmount = BigDecimal.ZERO;//默认最小借款额度为1000000
        if(amountLimitConfigMap!=null){
            String minAmountStr = amountLimitConfigMap.get(TradeConfigEnum.MINAMOUNT.getConfigKey());
            minAmount = (StringUtils.hasText(minAmountStr)?new BigDecimal(minAmountStr):minAmount);
        }
        log.info("getMinAmount.minAmount={}",minAmount);
        return minAmount;
    }

    //获取极速借授信总额度
    private BigDecimal getSpeedLoanCreditTotalAmount(String userId) {
        //获取授信总额度
        UserSpeedCreditInfoDTO userSpeedCreditInfoDTO = userCreditService.getUserSpeedCreditInfo(userId);
        BigDecimal speedLoanCreditTotalAmount = this.bigDecimalNullToZeroFormat(userSpeedCreditInfoDTO.getCreditTotalAmount());
        return speedLoanCreditTotalAmount;
        /*//22TODO:测试用
        return new BigDecimal("7000");*/
    }

    /**
     * 获取我的借款配置
     *
     * @return
     */
    @Override
    public SpeedLoanConfigurationResponseDTO speedLoanConfiguration(String userId) {
        //获取授信总额度
        BigDecimal speedLoanCreditTotalAmount = this.getSpeedLoanCreditTotalAmount(userId);
        Map<String, String> configMap = tradeConfigService.selectTradeConfigByEmuns(TradeConfigEnum.TITLE, TradeConfigEnum.MINAMOUNT, TradeConfigEnum.MAXAMOUNT);
        log.info("SpeedLoanDubboServiceImpl.speedLoanConfiguration.userId={},configMap={}",userId,JSON.toJSONString(configMap));
        BigDecimal aviAmount = this.getAviAmount(userId,speedLoanCreditTotalAmount,configMap);

        SpeedLoanConfigurationResponseDTO responseDTO = new SpeedLoanConfigurationResponseDTO(); //loanLocalService.speedLoanConfiguration(userId, aviAmount);

        responseDTO.setMaxLoanAmount(aviAmount);
        responseDTO.setMinLoanAmount(this.getMinAmount(userId,configMap));

        //设置借款可选标题
        responseDTO.setLoanTitleList(this.getLoanTitleList(configMap));
        responseDTO.setCreditTotalLoanAmount(speedLoanCreditTotalAmount);
        log.info("SpeedLoanDubboServiceImpl.speedLoanConfiguration.userId={},responseDTO={}",userId,JSON.toJSONString(responseDTO));
        return responseDTO;
    }

    private List<String> getLoanTitleList(Map<String, String> loanTitleConfigMap){
        if(loanTitleConfigMap==null){
            return null;
        }
        //设置借款可选标题
        String projectTitleDesc = loanTitleConfigMap.get(TradeConfigEnum.TITLE.getConfigKey());
        return StringUtils.hasText(projectTitleDesc) ? Arrays.asList(projectTitleDesc.split( ",")) : null;
    }

    private BigDecimal bigDecimalNullToZeroFormat(BigDecimal data) {
        return data == null ? new BigDecimal(0) : data;
    }

    //异常结果处理
    public void speedLoanPublishCheck_excetipnResponseDeal(LoanLimitMessageEnum loanLimitMessageEnum, SpeedLoanApplyCommonResponseDTO resposeDTO) {
        resposeDTO.setStatus(loanLimitMessageEnum.getLimitCode());
        resposeDTO.setAlertTitle(loanLimitMessageEnum.getLimitMessageTitle());
        resposeDTO.setAlertDesc(loanLimitMessageEnum.getLimitMessageDesc());
        throw new BizException("TRD10006");
    }

    //借款提交基本信息检查
    private SpeedLoanApplyCommonResponseDTO speedLoanApplyCheck(SpeedLoanApplyRequestDTO requestDTO) {
        SpeedLoanApplyCommonResponseDTO responseDTO = new SpeedLoanApplyCommonResponseDTO();

        //检查是否允许发布借款
        SpeedLoanPublishCheckRequestDTO speedLoanPublishCheckRequestDTO = new SpeedLoanPublishCheckRequestDTO();
        try {
            //借款每日总次数限制检查
            this.speedLoanEverydayLoanLimitCheck(requestDTO.getDeadline(), responseDTO);

            BeanUtils.copyProperties(requestDTO, speedLoanPublishCheckRequestDTO);
            speedLoanPublishCheckRequestDTO.setProjectApplyCheckTypeEnum(ProjectApplyCheckTypeEnum.NEW_PROJ_SUBMIT_CHECK);
            this.speedLoanPublishCheckMain(speedLoanPublishCheckRequestDTO, responseDTO);

            //借款配置数据检查
            this.speedLoanConfigurationCheck(requestDTO, responseDTO);
            responseDTO.setStatus(LoanLimitMessageEnum.LIMIT_10000.getLimitCode());
        } catch (BizException bizException) {
            log.error("speedLoanApplyCheck error", bizException);
            this.backProjectDetailUrlDeal(bizException,speedLoanPublishCheckRequestDTO,responseDTO);
        }

        return responseDTO;
    }

    /**
     * 借款每日总次数限制检查
     * @param deadline
     * @param resposeDTO
     */
    public void speedLoanEverydayLoanLimitCheck(int deadline, SpeedLoanApplyCommonResponseDTO resposeDTO) {
        if(!this.isNeedCheckForspeedLoanEverydayLoanLimit(deadline)){
            return;
        }
        String loanQuantityLimitKey = LOAN_QUANTITY_LIMIT_KEY+"_"+FastDateFormat.getInstance("yyyyMMdd").format(new Date());
        String loanQuantityLimit = redisTemplate.opsForValue().get(loanQuantityLimitKey);
        log.info("speedLoanEverydayLoanLimitCheck.loanQuantityLimit={}",loanQuantityLimit);
        if(!StringUtils.hasText(loanQuantityLimit)){
            return;
        }
        Map<String, String> tradeConfigMap = tradeConfigService.selectTradeConfigByEmuns(
                TradeConfigEnum.LOANQUANTITYLIMITDAILYAMOUNT,
                TradeConfigEnum.LOANQUANTITYLIMITPROMPTTEXT);
        log.info("speedLoanEverydayLoanLimitCheck.tradeConfigMap={}",JSON.toJSONString(tradeConfigMap));
        String loanQuantityLimitDailyAmount = tradeConfigMap.get(TradeConfigEnum.LOANQUANTITYLIMITDAILYAMOUNT.getConfigKey());
        if(Integer.parseInt(loanQuantityLimit)>=Integer.parseInt(loanQuantityLimitDailyAmount)){
            String loanQuantityLimitPromptText = tradeConfigMap.get(TradeConfigEnum.LOANQUANTITYLIMITPROMPTTEXT.getConfigKey());
            resposeDTO.setStatus(LoanLimitMessageEnum.LIMIT_10044.getLimitCode());
            resposeDTO.setAlertTitle(loanQuantityLimitPromptText);
            resposeDTO.setAlertDesc(loanQuantityLimitPromptText);
            throw new BizException("TRD10006");
        }
    }

    public SpeedLoanApplyCommonResponseDTO speedLoanConfirmSubmitCheck(int deadline) {
        SpeedLoanApplyCommonResponseDTO responseDTO = new SpeedLoanApplyCommonResponseDTO();
        try {
            //借款每日总次数限制检查
            this.speedLoanEverydayLoanLimitCheck(deadline, responseDTO);
            responseDTO.setStatus(LoanLimitMessageEnum.LIMIT_10000.getLimitCode());
        } catch (BizException bizException) {
            log.error("speedLoanConfirmSubmitCheck error", bizException);
        }

        return responseDTO;
    }

    private boolean isNeedCheckForspeedLoanEverydayLoanLimit(int deadline){
        Map<String, String> tradeConfigMap = tradeConfigService.selectTradeConfigByEmuns(TradeConfigEnum.LOANQUANTITYLIMITMINDEADLINE,
                TradeConfigEnum.LOANQUANTITYLIMITMAXDEADLINE,
                TradeConfigEnum.LOANQUANTITYLIMITDAILYAMOUNT,
                TradeConfigEnum.LOANQUANTITYLIMITPROMPTTEXT);
        log.info("isNeedCheckForspeedLoanEverydayLoanLimit.tradeConfigMap={}",JSON.toJSONString(tradeConfigMap));
        if(tradeConfigMap==null){
            return false;
        }
        String loanQuantityLimitMinDeadline = tradeConfigMap.get(TradeConfigEnum.LOANQUANTITYLIMITMINDEADLINE.getConfigKey());
        String loanQuantityLimitMaxDeadline = tradeConfigMap.get(TradeConfigEnum.LOANQUANTITYLIMITMAXDEADLINE.getConfigKey());
        String loanQuantityLimitDailyAmount = tradeConfigMap.get(TradeConfigEnum.LOANQUANTITYLIMITDAILYAMOUNT.getConfigKey());
        if(!StringUtils.hasText(loanQuantityLimitMinDeadline)
                || !StringUtils.hasText(loanQuantityLimitMaxDeadline)
                || !StringUtils.hasText(loanQuantityLimitDailyAmount)){
            return false;
        }
        if(Integer.parseInt(loanQuantityLimitMinDeadline)>deadline || Integer.parseInt(loanQuantityLimitMaxDeadline)<deadline){
            return false; //借款期限不在限制期限范围，则不做处理
        }
        return true;
    }

    /**
     * 申请极速借借款
     *
     * @param requestDTO
     * @return
     */
    @Override
    public SpeedLoanApplyCommonResponseDTO speedLoanApply(SpeedLoanApplyRequestDTO requestDTO) {
        String userId = requestDTO.getUserId();
        log.info("SpeedLoanDubboServiceImpl.speedLoanApply userId={},requestDTO={}",userId,JSON.toJSONString(requestDTO));

        //信息检查
        UserSpeedCreditInfoDTO userSpeedCreditInfoDTO = userCreditService.getUserSpeedCreditInfo(requestDTO.getUserId());
        log.info("SpeedLoanDubboServiceImpl.speedLoanApply userId={},userSpeedCreditInfoDTO={}",userId,JSON.toJSONString(userSpeedCreditInfoDTO));
        /*responseDTO = this.speedLoanApplyCheck(requestDTO, userSpeedCreditInfoDTO);*/
        SpeedLoanApplyCommonResponseDTO responseDTO = this.speedLoanApplyCheck(requestDTO);
        if (LoanLimitMessageEnum.LIMIT_10000.getLimitCode() != responseDTO.getStatus()) {
            return responseDTO;
        }

        //标的信息初始化
        Project insertProject = this.initProject(requestDTO, userSpeedCreditInfoDTO);
        Long projectId = snowflakeIdWorker.nextId();
        insertProject.setProjectId(projectId);

        //使用红包
        log.info("SpeedLoanDubboServiceImpl.speedLoanApply userId={} borrowPrizeOccupy start",userId);
        BorrowPrizeOccupyDTO borrowPrizeOccupyDTO = new BorrowPrizeOccupyDTO();
        BeanUtils.copyProperties(requestDTO,borrowPrizeOccupyDTO);
        this.borrowPrizeOccupy(borrowPrizeOccupyDTO,insertProject);
        log.info("SpeedLoanDubboServiceImpl.speedLoanApply userId={} borrowPrizeOccupy end",userId);
        
        //设置借款每日总次数限制
        this.setLoanQuantityLimit(requestDTO,responseDTO);

        //借款标的相关信息创建
        loanLocalService.loanProjectRelateInfoCreat(requestDTO, insertProject);
        Map<String, Object> extendData = responseDTO.getExtendData();
        if(extendData==null){
            extendData = new HashMap<>();
        }
        extendData.put("projectId",projectId);
        responseDTO.setExtendData(extendData);

        //借款相关信息数据回流
        log.info("SpeedLoanDubboServiceImpl.speedLoanApply userId={} loanApplyDataBackFlow start",userId);
        this.loanApplyDataBackFlow(userId,projectId);
        log.info("SpeedLoanDubboServiceImpl.speedLoanApply userId={} loanApplyDataBackFlow end",userId);

        //签订还款服务委托书合同
        log.info("SpeedLoanDubboServiceImpl.speedLoanApply userId={} fadadaWithholdContractSign start",userId);
        this.fadadaWithholdContractSign(userId,projectId);
        log.info("SpeedLoanDubboServiceImpl.speedLoanApply userId={} fadadaWithholdContractSign end",userId);

        //变更用户身份标示
        this.changeUserIdentityTag(userId,insertProject.getLoanTimes());

        //2017邀请好友活动发标事件处理
        this.inviteBestirActivityProjectPublish(insertProject);

        //标的状态变更事件回流
        try {
            ProjectStatusChangeBackFlowRequestDTO projectStatusChangeBackFlowRequestDTO = new ProjectStatusChangeBackFlowRequestDTO();
            projectStatusChangeBackFlowRequestDTO.setStatus(String.valueOf(ProjectSubStatusEnum.CHECKING_INIT.getSubStatus()));
            projectStatusChangeBackFlowRequestDTO.setFromStatus(String.valueOf(ProjectSubStatusEnum.CHECKING_INIT.getSubStatus()));
            projectStatusChangeBackFlowRequestDTO.setProjectId(insertProject.getProjectId());

            RpcContext.getContext().asyncCall(() -> tianChengDataBackFlowDubboService.projecctStatusChange(projectStatusChangeBackFlowRequestDTO));
        } catch (Exception e) {
            log.error("标的状态变更事件回流(申请中),标的ID={},发生系统异常,原因={}", insertProject.getProjectId(), e);
        }

        return responseDTO;
    }
    
    //设置借款每日总次数限制
    private void setLoanQuantityLimit(SpeedLoanApplyRequestDTO requestDTO,SpeedLoanApplyCommonResponseDTO responseDTO){
        if(!this.isNeedCheckForspeedLoanEverydayLoanLimit(requestDTO.getDeadline())){
            return;
        }
        Map<String, String> tradeConfigMap = tradeConfigService.selectTradeConfigByEmuns(
                TradeConfigEnum.LOANQUANTITYLIMITDAILYAMOUNT,
                TradeConfigEnum.LOANQUANTITYLIMITPROMPTTEXT);
        String loanQuantityLimitDailyAmount = tradeConfigMap.get(TradeConfigEnum.LOANQUANTITYLIMITDAILYAMOUNT.getConfigKey());
        String loanQuantityLimitKey = LOAN_QUANTITY_LIMIT_KEY+"_"+FastDateFormat.getInstance("yyyyMMdd").format(new Date());
        long loanQuantityLimitCount = redisTemplate.opsForValue().increment(loanQuantityLimitKey,1l);
        log.info("setLoanQuantityLimit.loanQuantityLimitCount={}",loanQuantityLimitCount);
        redisTemplate.expire(loanQuantityLimitKey, this.getLeftSecondsToday(), TimeUnit.SECONDS);
        if(loanQuantityLimitCount>Integer.parseInt(loanQuantityLimitDailyAmount)){
            String loanQuantityLimitPromptText = tradeConfigMap.get(TradeConfigEnum.LOANQUANTITYLIMITPROMPTTEXT.getConfigKey());
            responseDTO.setStatus(LoanLimitMessageEnum.LIMIT_10044.getLimitCode());
            responseDTO.setAlertTitle(loanQuantityLimitPromptText);
            responseDTO.setAlertDesc(loanQuantityLimitPromptText);
            throw new BizException("TRD10006");
        }
    }

    /**
     * 获取当天剩余秒数
     */
    private long getLeftSecondsToday() {
        LocalDate now = new LocalDate();
        return new Duration(now.toDateTimeAtCurrentTime(), now.plusDays(1).toDateTimeAtStartOfDay()).getStandardSeconds();
    }

    private void changeUserIdentityTag(String userId,Integer loanTimes){
        log.info("changeUserIdentityTag userId={},loanTimes={} start",userId,loanTimes);
        if(loanTimes.intValue()>0){
            log.info("changeUserIdentityTag userId={},loanTimes={},首次借款才设置",userId,loanTimes);
            return;
        }
        try {
            CompletableFuture.runAsync(() -> userBasicInfoDubboService.changeUserIdentityTag(userId,UserIdentityEnum.BORROWER));
            log.info("changeUserIdentityTag userId={},loanTimes={} success end",userId,loanTimes);
        } catch (Exception e) {
            log.error("changeUserIdentityTag userId={},loanTimes={} error",userId,loanTimes,e);
        }
    }

    private void inviteBestirActivityProjectPublish(Project project){
        String userId = project.getBorrowUserId();
        log.info("SpeedLoanDubboServiceImpl.inviteBestirActivityProjectPublish.userId={} start",userId);
        try {
            ProjectPublishEventDTO projectPublishEventDTO = new ProjectPublishEventDTO();
            projectPublishEventDTO.setUserId(userId);
            projectPublishEventDTO.setProjectId(project.getProjectId());
            projectPublishEventDTO.setProjectType(com.niiwoo.activity.stage.enums.inviterBuddy.ProjectTypeEnum.findForValue(Integer.valueOf(project.getType())));
            projectPublishEventDTO.setDateTime(project.getCreateTime());
            log.info("SpeedLoanDubboServiceImpl.inviteBestirActivityProjectPublish.userId={},projectPublishEventDTO={}",userId,JSON.toJSONString(projectPublishEventDTO));
            CompletableFuture.runAsync(() -> inviteBuddyActivityDubboService.inviteBestirActivityProjectPublish(projectPublishEventDTO));
            log.info("SpeedLoanDubboServiceImpl.inviteBestirActivityProjectPublish.userId={} end",userId);
        } catch (Exception e) {
            log.info("SpeedLoanDubboServiceImpl.inviteBestirActivityProjectPublish.userId={} error",userId,e);
        }
    }

    //借款红包占用
    private void borrowPrizeOccupy(BorrowPrizeOccupyDTO borrowPrizeOccupyDTO, Project project){
        if(borrowPrizeOccupyDTO==null || borrowPrizeOccupyDTO.getPrizeId()==null){
            return;
        }

        String userId = borrowPrizeOccupyDTO.getUserId();
        Long projectId = project.getProjectId();
        Long prizeId = borrowPrizeOccupyDTO.getPrizeId();
        log.info("borrowPrizeOccupy.userId={},projectId={},prizeId={} start",userId,projectId,prizeId);

        //红包占用本地日志记录生成
        ProjectBorrowerPrizeOccupyRecord projectBorrowerPrizeOccupyRecord = loanLocalService.buildProjectBorrowerPrizeOccupyRecord(borrowPrizeOccupyDTO, projectId);

        //调用活动接口占用红包
        ActivityPrizeStatusReqDTO prizeRequestDTO = new ActivityPrizeStatusReqDTO();
        prizeRequestDTO.setOrderNo(projectBorrowerPrizeOccupyRecord.getOrderNo());
        prizeRequestDTO.setUserId(userId);
        prizeRequestDTO.setPrizeId(prizeId);
        prizeRequestDTO.setProjectId(projectId);
        prizeRequestDTO.setDeadLine(borrowPrizeOccupyDTO.getDeadline());
        prizeRequestDTO.setAmount(borrowPrizeOccupyDTO.getLoanAmount());
        prizeRequestDTO.setSuitProjectTypeEnum(UserPrizeSuitProjectTypeEnum.SPEED);

        boolean prizeOccupyResult = true;
        try {
            log.info("borrowPrizeOccupy.userId={},projectId={},prizeId={},prizeRequestDTO={}",userId,projectId,prizeId,JSON.toJSONString(prizeRequestDTO));
            prizeOccupyResult = activityPrizeDubboService.setPrizeUsedToOccupied(prizeRequestDTO);
        } catch (Exception e) {
            log.error("borrowPrizeOccupy.userId={},projectId={},prizeId={} error,prizeOccupyResult={}",userId,projectId,prizeId,prizeOccupyResult,e);
            loanLocalService.updateProjectBorrowerPrizeOccupyRecordStatus(projectBorrowerPrizeOccupyRecord,BorrowPrizeOccupyEnum.OccupyStatusEnum.ERROR);
            //出现异常情况下需要退回红包
            this.resetPrizeToReturned(userId,prizeId);
            throw new BizException("TRD10011");
        }
        log.info("borrowPrizeOccupy.userId={},projectId={},prizeId={},prizeOccupyResult={}",userId,projectId,prizeId,prizeOccupyResult);
        if(!prizeOccupyResult){
            log.info("borrowPrizeOccupy.userId={},projectId={},prizeId={},借款提交占用红包失败,prizeOccupyResult={}",userId,projectId,prizeId,prizeOccupyResult);
            loanLocalService.updateProjectBorrowerPrizeOccupyRecordStatus(projectBorrowerPrizeOccupyRecord,BorrowPrizeOccupyEnum.OccupyStatusEnum.FAIL);
            throw new BizException("TRD10011");
        }else{
            loanLocalService.updateProjectBorrowerPrizeOccupyRecordStatus(projectBorrowerPrizeOccupyRecord,BorrowPrizeOccupyEnum.OccupyStatusEnum.SUCCESS);
        }

        project.setPrizeId(prizeId);
        log.info("borrowPrizeOccupy.userId={},projectId={},prizeId={} end",userId,projectId,prizeId);
    }

    //红包退回
    private void resetPrizeToReturned(String userId,Long prizeId){
        log.info("resetPrizeToReturned.userId={},prizeId={} start",userId,prizeId);
        try {
            ActivityPrizeResetStatusReqDTO reqDTO = new ActivityPrizeResetStatusReqDTO();
            reqDTO.setUserId(userId);
            reqDTO.setPrizeId(prizeId);
            reqDTO.setOrderNo(snowflakeIdWorker.nextId());
            CompletableFuture.runAsync(() -> activityPrizeDubboService.resetPrizeToReturned(reqDTO));
        } catch (Exception e) {
            log.error("resetPrizeToReturned.userId={},prizeId={} error",userId,prizeId,e);
        }
        log.info("resetPrizeToReturned.userId={},prizeId={} end",userId,prizeId);
    }

    //借款相关信息数据回流
    private void loanApplyDataBackFlow(String userId,Long projectId){
        log.info("SpeedLoanDubboServiceImpl.loanApplyDataBackFlow userId={},projectId={} start",userId,projectId);
        try {
            LoanApplyDataBackFlowRequestDTO loanApplyDataBackFlowRequestDTO = new LoanApplyDataBackFlowRequestDTO();
            loanApplyDataBackFlowRequestDTO.setUserId(userId);
            loanApplyDataBackFlowRequestDTO.setProjectId(projectId);

            //RpcContext.getContext().asyncCall(() -> tianChengDataBackFlowDubboService.loanApplyDataBackFlow(loanApplyDataBackFlowRequestDTO));
            CompletableFuture.runAsync(() -> tianChengDataBackFlowDubboService.loanApplyDataBackFlow(loanApplyDataBackFlowRequestDTO));
            log.info("SpeedLoanDubboServiceImpl.loanApplyDataBackFlow userId={},projectId={} success end",userId,projectId);
        } catch (Exception e) {
            log.info("SpeedLoanDubboServiceImpl.loanApplyDataBackFlow userId={},projectId={} error",userId,projectId,e);
        }
    }

    /**
     * 用户申请借款配置数据检查
     *
     * @param request
     */
    private void speedLoanConfigurationCheck(SpeedLoanApplyRequestDTO request, SpeedLoanApplyCommonResponseDTO response) {
        loanLocalService.speedLoanConfigurationCheck(request, response);
    }

    private Project initProject(SpeedLoanApplyRequestDTO speedLoanApplyRequestDTO, UserSpeedCreditInfoDTO userSpeedCreditInfoDTO) {
        Project project = new Project();

        String userId = speedLoanApplyRequestDTO.getUserId();
        Date now = new Date();

        // 获取用户基本信息
        UserBasicInfoResponseDTO userBasicInfo = userBasicInfoDubboService.getUserBasicInfo(userId);

        // 获取用户身份证信息
        UserIdentityInfoResponseDTO userIdentityInfo = userIdentityDubboService.get(userId);

        // 获取用户授权相关信息
        UserAuthorizationInfoResponseDTO userAuthorizationInfo = userAuthorizationInfoDubboService.getUserAuthorizationInfo(userId);

        // 获取借款成功次数
        int loanTime = projectCommonService.statisticsProjectCountByStatus(userId, Arrays.asList(ProjectStatusEnum.REPAYING.getStatus(), ProjectStatusEnum.FINISH.getStatus()), null);

        Byte contactsBackflowStatus = ContactsBackflowStatusEnum.NO.getValue();
        if (ProjectFromSourceEnum.R360.getType().byteValue() == speedLoanApplyRequestDTO.getFromSource().byteValue()) {// 融360设置为已回流
            contactsBackflowStatus = ContactsBackflowStatusEnum.SUCCESS.getValue();
        }

        AccountBaseResponse accountBaseResponse = accountDubboService.loadNiiwooAccount(userId, UserRoleEnum.BORROWERS);

        project.setOrgId(OrgConstant.NIIWOO_ORG_ID);
        project.setBorrowAccountId(accountBaseResponse.getAccountId());
        project.setBorrowPlatformUserNo(accountBaseResponse.getAccountNo());
        project.setBorrowUserId(userId); // 借款用户
        project.setBorrowUserName(userIdentityInfo.getName()); // 借款人姓名
        project.setBorrowMobile(this.ecryptString(userBasicInfo.getMobileNo())); // 借款人手机号，加密
        project.setBorrowIdCard(this.ecryptString(userIdentityInfo.getIdCard())); // 身份证号，加密
        project.setBorrowHeadImage(userBasicInfo.getHeadImage()); // 头像
        project.setBorrowNickName(userBasicInfo.getNickName()); // 昵称
        project.setBorrowGender(userBasicInfo.getGender()); // 性别
        project.setBorrowNiiwooScore(userBasicInfo.getNiiwooScore()); //  你我分
        project.setBorrowerType(BorrowerTypeEnum.PERSONAL.getType());
        project.setIsMasterBorrower((byte) 1); // 是否主借人(0.否;1.是)

        Integer deadline = speedLoanApplyRequestDTO.getDeadline();
        ProjectRateConfig projectRateConfig = speedLoanRateConfigService.getRateConfigByDeadline(deadline);

        project.setRepaymentType(projectRateConfig.getRepaymentMode());//还款方式1、到期还本付息，2、等额本息
        project.setDeadline(deadline); // 期数
        project.setDeadlineUnit(DeadlineUnitEnum.BY_MONTH.getDeadlineUnit()); // 期数单位(1:月，2:天)
        project.setTitle("借款用途：" + speedLoanApplyRequestDTO.getLoanTitle()); // 项目标题
        project.setContractAmount(speedLoanApplyRequestDTO.getLoanAmount()); // 合同金额,也即标的金额
        project.setHandAmount(speedLoanApplyRequestDTO.getLoanAmount()); // 到手金额
//        project.setChannelCode(ChannelCodeEnum.APP.getChanelCode());
        project.setLoanTimes(loanTime);  // 借款成功次数

        project.setStatus(ProjectSubStatusEnum.CHECKING_INIT.getParentStatusEnum().getStatus()); // 项目状态（1待发布、2审核中、3筹款中、4还款中、5已结清、6流标）
        project.setSubStatus(ProjectSubStatusEnum.CHECKING_INIT.getSubStatus()); // 子状态，如100，200
        project.setType(com.niiwoo.civet.trade.enums.ProjectTypeEnum.PERSONAL_SPEEDLOAN.getValue());  // 类型(默认1:个人极速借2:机构极速借)
        project.setFromSource(speedLoanApplyRequestDTO.getFromSource()); // 标的来源（1、APP，2、H5活动，3、H5极速借,4、融360）
        project.setZhimaScore(userAuthorizationInfo.getZhimaScore()); // 芝麻分(冗余)
        project.setContactsBackflowStatus(contactsBackflowStatus); // 通讯录回流状态(0、未回流1、已回流2、回流失败)
        project.setCreditType(userSpeedCreditInfoDTO.getCreditModelType()); // 授信额度类型(1:社保,2:芝麻,3:学历)
        project.setLoanType(LoanTypeEnum.SPEED_LOAN.getValue());
        project.setLongitude(speedLoanApplyRequestDTO.getLongitude()); // 经度
        project.setLatitude(speedLoanApplyRequestDTO.getLatitude()); // 纬度
        project.setLoanProvince(speedLoanApplyRequestDTO.getProvince()); // 借款省
        project.setLoanCity(speedLoanApplyRequestDTO.getCity());  // 借款市
        project.setLoanArea(speedLoanApplyRequestDTO.getArea());  // 借款区
        project.setSubmitTime(now);  // 借款人提交到后台审核时间
        project.setCreateTime(now);  // 添加时间
        project.setUpdateTime(now);  // 更新时间

        // 费率相关
        project.setBorrowRate(projectRateConfig.getBorrowInterestRate()); //借款利率

        String penaltyRateStr = tradeConfigService.selectTradeConfigByEmun(TradeConfigEnum.OVERDUEFEERATE);
        project.setPenaltyRate(new BigDecimal(penaltyRateStr)); // 预期罚息率

        project.setBeforeSeekAdviceServiceFeeRate(BigDecimal.ZERO); // 前置咨询服务费率
        project.setBeforeSeekAdviceServiceFee(BigDecimal.ZERO);// 前置服务费

        project.setGuaranteeFeeChargeNode((byte) 3); // 担保费收取节点(1.放款时;2.还款第一期;3.还款每期)
        project.setManageFeeChargeNode((byte) 3); // 平台管理费收取节点(1.放款时;2.还款第一期;3.还款每期)
        project.setBeforeManageRate(BigDecimal.ZERO);// 前置平台管理费率
        project.setBeforeManageFee(BigDecimal.ZERO);// 前置平台管理费

        project.setAfterSeekAdviceServiceFeeChargeNode((byte) 3); // 后置咨询服务费收取节点(1.放款时;2.还款第一期;3.还款每期)
        project.setConsultingRate(BigDecimal.ZERO); // 后置咨询服务费率
        project.setConsultingFee(BigDecimal.ZERO);  // 后置咨询服务费

        project.setManageRate(projectRateConfig.getPlatformManageFeeRate()); // 后置平台管理费率
        project.setGuaranteeRate(projectRateConfig.getGuaranteeServiceFeeRate()); // 后置担保费率

        BigDecimal afterPlatformManageFee = project.getContractAmount().multiply(project.getManageRate()).setScale(2, BigDecimal.ROUND_HALF_EVEN);
        BigDecimal afterGuaranteeServiceFee = project.getContractAmount().multiply(project.getGuaranteeRate()).setScale(2, BigDecimal.ROUND_HALF_EVEN);

        project.setManageFee(afterPlatformManageFee); // 后置平台管理费
        project.setGuaranteeFee(afterGuaranteeServiceFee); // 后置担保费

        // 初次申请金额、期限
        project.setFirstDeadline(project.getDeadline());
        project.setFirstDeadlineUnit(project.getDeadlineUnit());
        project.setFirstAmount(project.getContractAmount());

        return project;
    }

    /**
     * 退回标的申请极速借借款
     *
     * @param requestDTO
     * @return
     */
    public SpeedLoanApplyCommonResponseDTO speedLoanApplyForBack(SpeedLoanApplyForBackRequestDTO requestDTO) {
        log.info("speedLoanApplyForBack.requestDTO={}",JSON.toJSONString(requestDTO));
        String userId = requestDTO.getUserId();
        Long projectId = requestDTO.getProjectId();
        log.info("speedLoanApplyForBack.userId={} start",userId);

        //信息检查
        UserSpeedCreditInfoDTO userSpeedCreditInfoDTO = userCreditService.getUserSpeedCreditInfo(requestDTO.getUserId());
        /*responseDTO = this.speedLoanApplyForBackCheck(requestDTO, userSpeedCreditInfoDTO);*/
        log.info("speedLoanApplyForBack.userId={} speedLoanApplyForBackCheck start",userId);
        SpeedLoanApplyCommonResponseDTO responseDTO = this.speedLoanApplyForBackCheck(requestDTO);
        log.info("speedLoanApplyForBack.userId={} speedLoanApplyForBackCheck responseDTO={}",userId,JSON.toJSONString(responseDTO));
        if (10000 != responseDTO.getStatus()) {
            return responseDTO;
        }

        SpeedLoanApplyRequestDTO speedLoanApplyRequestDTO = new SpeedLoanApplyRequestDTO();
        BeanUtils.copyProperties(requestDTO, speedLoanApplyRequestDTO);
        Project updateProject = this.initProject(speedLoanApplyRequestDTO, userSpeedCreditInfoDTO);
        updateProject.setProjectId(projectId);

        //使用红包
        log.info("speedLoanApplyForBack.userId={} borrowPrizeOccupy start",userId);
        BorrowPrizeOccupyDTO borrowPrizeOccupyDTO = new BorrowPrizeOccupyDTO();
        BeanUtils.copyProperties(requestDTO,borrowPrizeOccupyDTO);
        this.borrowPrizeOccupy(borrowPrizeOccupyDTO,updateProject);
        log.info("speedLoanApplyForBack.userId={} borrowPrizeOccupy end",userId);

        //借款标的相关信息更新
        loanLocalService.loanProjectRelateInfoUpdate(requestDTO, updateProject);

        Map<String, Object> extendData = responseDTO.getExtendData();
        if(extendData==null){
            extendData = new HashMap<>();
        }
        extendData.put("projectId",projectId);
        responseDTO.setExtendData(extendData);


        //借款相关信息数据回流
        this.loanApplyDataBackFlow(requestDTO.getUserId(),projectId);

        //签订还款服务委托书合同
        this.fadadaWithholdContractSign(userId,projectId);

        //变更用户身份标示
        this.changeUserIdentityTag(userId,updateProject.getLoanTimes());

        log.info("speedLoanApplyForBack.userId={} responseDTO={}",userId,JSON.toJSONString(responseDTO));
        return responseDTO;
    }

    /**
     * 退回标的信息查询
     * @param userId
     * @return
     */
    public SpeedLoanQueryResponseDTO speedLoanQueryForBack(String userId){
        return loanLocalService.speedLoanQueryForBack(userId);
    }



    @Override
    public SpeedLoanCreditAmountDTO getSpeedLoanCreditAmount(String userId) {
        SpeedLoanCreditAmountDTO speedLoanCreditAmount = new SpeedLoanCreditAmountDTO();
        // 获取用户额度信息
        UserSpeedCreditInfoDTO userSpeedCreditInfo = userCreditService.getUserSpeedCreditInfo(userId);
        BigDecimal creditAmount = userSpeedCreditInfo.getCreditTotalAmount();
        if (creditAmount != null) {
            speedLoanCreditAmount.setCreditTotalAmount(creditAmount);
            BigDecimal usedAmount = loanLocalService.getSpeedUsedCredit(userId); // 已使用额度
            if (usedAmount != null) {
                speedLoanCreditAmount.setCreditUsedAmount(usedAmount);
            }
        } else {
            speedLoanCreditAmount.setCreditTotalAmount(BigDecimal.ZERO);
            speedLoanCreditAmount.setCreditUsedAmount(BigDecimal.ZERO);
        }
        return speedLoanCreditAmount;
    }

    @Override
    public void r360RecallAutoAuditModelJob() {
        List<Project> projectList = projectLocalService.selectNeedRecallAutoAuditModelRecord();
        projectList.stream().forEach(project -> {
            try {
                callAutoAuditModel(project);
            } catch (Exception e) {
                log.error("融360标的补偿调用天称信审要点,标的ID={},发生异常:", project.getProjectId(), e);
            }
        });
    }

    /**
     * 调用天称信审要点
     *
     * @param project
     */
    private void callAutoAuditModel(Project project) {
        UserBasicInfoResponseDTO userBasicInfoResponseDTO = userBasicInfoDubboService.getUserBasicInfo(project.getBorrowUserId());
        if (userBasicInfoResponseDTO == null) {
            log.error("融360标的补偿调用天称信审要点,标的ID={},发生异常,原因是用户基础信息不存在.", project.getProjectId());
            return;
        }
        UserPersonalDubboInfoDTO userPersonalDubboInfoDTO = userDataDubboService.userPersonalInfo(project.getBorrowUserId());
        if (userPersonalDubboInfoDTO == null) {
            log.error("融360标的补偿调用天称信审要点,标的ID={},发生异常,原因是用户个人信息不存在.", project.getProjectId());
            return;
        }

        UserIdentityInfoResponseDTO userIdentityInfoResponseDTO = userIdentityDubboService.get(project.getBorrowUserId());
        if (userIdentityInfoResponseDTO == null) {
            log.error("融360标的补偿调用天称信审要点,标的ID={},发生异常,原因是用户身份信息不存在.", project.getProjectId());
            return;
        }

        SpeedLoanCreditAuditRequest speedLoanCreditAuditRequest = new SpeedLoanCreditAuditRequest();
        speedLoanCreditAuditRequest.setUser_id(String.valueOf(userBasicInfoResponseDTO.getUserId()));
        speedLoanCreditAuditRequest.setProjectId(String.valueOf(project.getProjectId()));
        speedLoanCreditAuditRequest.setIdtype(SpeedLoanCreditAuditRequest.IdTypeEnum.IDENTITY_CARD.getType());
        speedLoanCreditAuditRequest.setApplyDate(DateFormatUtils.format(new Date(), "yyyy-MM-dd"));
        speedLoanCreditAuditRequest.setDegree(transEducationLevel2Degree(userPersonalDubboInfoDTO.getEducationLevel()));
        speedLoanCreditAuditRequest.setIdentity_card(userIdentityInfoResponseDTO.getIdCard());
        speedLoanCreditAuditRequest.setName(userIdentityInfoResponseDTO.getName());
        speedLoanCreditAuditRequest.setMobile_phone(privacyMasks.decryptPrivacy(project.getBorrowMobile()));

        List<RelationVerificationDTO> relationVerificationDTOList = userDataDubboService.getRelationVerification(userBasicInfoResponseDTO.getUserId());
        log.info("融360标的补偿调用天称信审要点,标的ID={},查询用户关系人结果={}.", project.getProjectId(), JSON.toJSONString(relationVerificationDTOList));

        speedLoanCreditAuditRequest.setRELATION_VERIFICATION(transRelationVerificationDTO(relationVerificationDTOList));
        UserAuthorizationInfoResponseDTO userAuthorizationInfoResponseDTO = userAuthorizationInfoDubboService.getUserAuthorizationInfo(userBasicInfoResponseDTO.getUserId());
        log.info("融360标的补偿调用天称信审要点,标的ID={},获取用户授权相关信息结果={}.", project.getProjectId(), JSON.toJSONString(userAuthorizationInfoResponseDTO));

        speedLoanCreditAuditRequest.setZhima_score(userAuthorizationInfoResponseDTO.getZhimaScore());
        log.info("融360标的补偿调用天称信审要点,标的ID={},获取用户身份信息结果={}.", project.getProjectId(), JSON.toJSONString(userIdentityInfoResponseDTO));
        speedLoanCreditAuditRequest.setId_expired_date(userIdentityInfoResponseDTO.getEndDate() == null ? null : DateFormatUtils.format(userIdentityInfoResponseDTO.getEndDate(), "yyyy-MM-dd"));
        speedLoanCreditAuditRequest.setPlatform_type(project.getFromSource().equals(ProjectFromSourceEnum.R360.getType()) ? PlatformTypeEnum.R360.getType() : PlatformTypeEnum.NIIWOO.getType());
        speedLoanCreditAuditRequest.setExt_cutype(userCreditService.isOldUser(userBasicInfoResponseDTO.getUserId()));
        speedLoanCreditAuditRequest.setFace_recognition_type(userAuthorizationInfoResponseDTO.getFaceVerificationChannel().intValue());
        SearchFacePlusPortraitResponseDTO searchFacePlusPortraitResponseDTO = portraitAuthDubboService.searchFacePlusPortraitResponse(userBasicInfoResponseDTO.getUserId());
        log.info("融360标的补偿调用天称信审要点,标的ID={},查询肖像认证信息结果={}.", project.getProjectId(), JSON.toJSONString(searchFacePlusPortraitResponseDTO));

        if (searchFacePlusPortraitResponseDTO != null) {
            speedLoanCreditAuditRequest.setId_positive_water_lines(searchFacePlusPortraitResponseDTO.getIdImageConfidence());
            speedLoanCreditAuditRequest.setBest_face_or_blink_eyes(searchFacePlusPortraitResponseDTO.getLiveImageConfidence());
        }
        speedLoanCreditAuditRequest.setNotify_server_url(String.format(TIANCHENG_AFP_CALLBACK_URL, webHttpUrl, TianchengCallbackEventEnum.SPEED_LOAN_AUDIT_POINT_FAKE.getEventType(), project.getProjectId(), FunctionCodeEnum.LIBRA_FRAUD_SPEED_LOAN_EXAMINE.getBodyFunctionCode()));
        speedLoanCreditAuditRequest.setTransNo(String.valueOf(snowflakeIdWorker.nextId()));
        speedLoanCreditAuditRequest.setTransDate(new Date());

        loanLocalService.toRequestAutoAuditModel(speedLoanCreditAuditRequest);
    }

    @Override
    public void loanAgainstFakeCallback(LoanAgainstFackRequestDTO loanAgainstFackRequestDTO) {
        HintManager hintManager = HintManager.getInstance();
        hintManager.setMasterRouteOnly();
        log.info("借款反欺诈回调,标的ID={},内部方法请求参数={}", loanAgainstFackRequestDTO.getProjectId(), JSON.toJSONString(loanAgainstFackRequestDTO));
        Project project = projectCommonService.queryProjectBasicInfo(loanAgainstFackRequestDTO.getProjectId());
        if (project == null) {
            log.error("借款反欺诈回调,标的ID={},发生异常,原因是标的不存在.", loanAgainstFackRequestDTO.getProjectId());
            return;
        }

        if (!(project.getStatus().equals(ProjectStatusEnum.CHECKING.getStatus()) &&
                (project.getSubStatus().equals(ProjectSubStatusEnum.CHECKING_INIT.getSubStatus()) || project.getSubStatus().equals(ProjectSubStatusEnum.CHECKING_BACK.getSubStatus())))) {
            log.error("借款反欺诈回调,标的ID={},发生异常,原因是标的状态不在审核中.", loanAgainstFackRequestDTO.getProjectId());
            return;
        }

        UserBasicInfoResponseDTO userBasicInfoResponseDTO = userBasicInfoDubboService.getUserBasicInfo(project.getBorrowUserId());
        log.info("借款反欺诈回调,标的ID={},请求结果={}", loanAgainstFackRequestDTO.getProjectId(), JSON.toJSONString(userBasicInfoResponseDTO));

        if (userBasicInfoResponseDTO == null) {
            log.error("借款反欺诈回调,标的ID={},发生异常,原因是用户基础信息不存在.", loanAgainstFackRequestDTO.getProjectId());
            return;
        }
        UserPersonalDubboInfoDTO userPersonalDubboInfoDTO = userDataDubboService.userPersonalInfo(project.getBorrowUserId());
        if (userPersonalDubboInfoDTO == null) {
            log.error("借款反欺诈回调,标的ID={},发生异常,原因是用户个人信息不存在.", loanAgainstFackRequestDTO.getProjectId());
            return;
        }

        UserIdentityInfoResponseDTO userIdentityInfoResponseDTO = userIdentityDubboService.get(project.getBorrowUserId());
        if (userIdentityInfoResponseDTO == null) {
            log.error("借款反欺诈回调,标的ID={},发生异常,原因是用户身份信息不存在.", loanAgainstFackRequestDTO.getProjectId());
            return;
        }

        Integer result = null;
        //1.根据反欺诈结果做出相应处理,分别如下:
        //1.1 成功
        if (loanAgainstFackRequestDTO.isSuccess()) {
            log.info("借款反欺诈回调,标的ID={},成功.", loanAgainstFackRequestDTO.getProjectId());
            //1.1.1 发起天称审核要点反欺诈;
            SpeedLoanCreditAuditRequest speedLoanCreditAuditRequest = new SpeedLoanCreditAuditRequest();
            speedLoanCreditAuditRequest.setUser_id(String.valueOf(userBasicInfoResponseDTO.getUserId()));
            speedLoanCreditAuditRequest.setProjectId(String.valueOf(loanAgainstFackRequestDTO.getProjectId()));
            speedLoanCreditAuditRequest.setIdtype(SpeedLoanCreditAuditRequest.IdTypeEnum.IDENTITY_CARD.getType());
            speedLoanCreditAuditRequest.setApplyDate(DateFormatUtils.format(new Date(), "yyyy-MM-dd"));
            speedLoanCreditAuditRequest.setDegree(transEducationLevel2Degree(userPersonalDubboInfoDTO.getEducationLevel()));
            speedLoanCreditAuditRequest.setIdentity_card(userIdentityInfoResponseDTO.getIdCard());
            speedLoanCreditAuditRequest.setName(userIdentityInfoResponseDTO.getName());
            speedLoanCreditAuditRequest.setMobile_phone(privacyMasks.decryptPrivacy(project.getBorrowMobile()));

            List<RelationVerificationDTO> relationVerificationDTOList = userDataDubboService.getRelationVerification(userBasicInfoResponseDTO.getUserId());
            log.info("借款反欺诈回调,标的ID={},查询用户关系人结果={}.", loanAgainstFackRequestDTO.getProjectId(), JSON.toJSONString(relationVerificationDTOList));

            speedLoanCreditAuditRequest.setRELATION_VERIFICATION(transRelationVerificationDTO(relationVerificationDTOList));
            UserAuthorizationInfoResponseDTO userAuthorizationInfoResponseDTO = userAuthorizationInfoDubboService.getUserAuthorizationInfo(userBasicInfoResponseDTO.getUserId());
            log.info("借款反欺诈回调,标的ID={},获取用户授权相关信息结果={}.", loanAgainstFackRequestDTO.getProjectId(), JSON.toJSONString(userAuthorizationInfoResponseDTO));

            speedLoanCreditAuditRequest.setZhima_score(userAuthorizationInfoResponseDTO.getZhimaScore());
            log.info("借款反欺诈回调,标的ID={},获取用户身份信息结果={}.", loanAgainstFackRequestDTO.getProjectId(), JSON.toJSONString(userIdentityInfoResponseDTO));
            speedLoanCreditAuditRequest.setId_expired_date(userIdentityInfoResponseDTO.getEndDate()==null?null:DateFormatUtils.format(userIdentityInfoResponseDTO.getEndDate(), "yyyy-MM-dd"));
            speedLoanCreditAuditRequest.setPlatform_type(project.getFromSource().equals(ProjectFromSourceEnum.R360.getType()) ? PlatformTypeEnum.R360.getType() : PlatformTypeEnum.NIIWOO.getType());
            speedLoanCreditAuditRequest.setExt_cutype(userCreditService.isOldUser(userBasicInfoResponseDTO.getUserId()));
            speedLoanCreditAuditRequest.setFace_recognition_type(userAuthorizationInfoResponseDTO.getFaceVerificationChannel().intValue());
            SearchFacePlusPortraitResponseDTO searchFacePlusPortraitResponseDTO = portraitAuthDubboService.searchFacePlusPortraitResponse(userBasicInfoResponseDTO.getUserId());
            log.info("借款反欺诈回调,标的ID={},查询肖像认证信息结果={}.", loanAgainstFackRequestDTO.getProjectId(), JSON.toJSONString(searchFacePlusPortraitResponseDTO));

            if(searchFacePlusPortraitResponseDTO!=null) {
                speedLoanCreditAuditRequest.setId_positive_water_lines(searchFacePlusPortraitResponseDTO.getIdImageConfidence());
                speedLoanCreditAuditRequest.setBest_face_or_blink_eyes(searchFacePlusPortraitResponseDTO.getLiveImageConfidence());
            }
            speedLoanCreditAuditRequest.setNotify_server_url(String.format(TIANCHENG_AFP_CALLBACK_URL, webHttpUrl, TianchengCallbackEventEnum.SPEED_LOAN_AUDIT_POINT_FAKE.getEventType(), project.getProjectId(), FunctionCodeEnum.LIBRA_FRAUD_SPEED_LOAN_EXAMINE.getBodyFunctionCode()));
            speedLoanCreditAuditRequest.setTransNo(String.valueOf(snowflakeIdWorker.nextId()));
            speedLoanCreditAuditRequest.setTransDate(new Date());

            loanLocalService.toRequestAutoAuditModel(speedLoanCreditAuditRequest);
        }
        //1.2 不通过
        else {
            log.info("借款反欺诈回调,标的ID={},不通过.", loanAgainstFackRequestDTO.getProjectId());

            ProjectFlowRequestDTO requestDTO = new ProjectFlowRequestDTO();
            requestDTO.setProjectId(project.getProjectId());
            requestDTO.setSubStatus(ProjectSubStatusEnum.MISCARRY_LOAN_FAKE_FAILED);
            projectDubboService.flow(requestDTO);

            //1.2.3 限制用户借款行为
            AddUserLoanLimitRequestDTO addUserLoanLimitRequestDTO = new AddUserLoanLimitRequestDTO();
            addUserLoanLimitRequestDTO.setLimitDays(Integer.valueOf(tradeConfigService.selectTradeConfigByEmun(TradeConfigEnum.HITLOANANTIFRAUDLIMITDAYS)));
            addUserLoanLimitRequestDTO.setLimitType(LibraLoanLimitEnum.LOAN_AGAINST_FAKE.getLimitType().intValue());
            addUserLoanLimitRequestDTO.setUserId(project.getBorrowUserId());
            addUserLoanLimitRequestDTO.setProductTypeEnum(ProductTypeEnum.SPEED_LOAN);

            userForbidDubboService.addUserLibraLoanLimit(addUserLoanLimitRequestDTO);

            if (loanLocalService.isR360(project.getFromSource())) {
                R360OrderStatusChangedDTO r360OrderStatusChangedDTO = new R360OrderStatusChangedDTO();
                r360OrderStatusChangedDTO.setProjectId(project.getProjectId());
                r360OrderStatusChangedDTO.setUpdateDate(new Date());
                r360OrderStatusChangedDTO.setR360OrderStatusEnumCode(ProjectStatusEnum.MISCARRY.getStatus().intValue());
                r360OrderStatusChangedDTO.setR360OrderProjectSubStatusEnum(R360OrderProjectSubStatusEnum.MISCARRY_LOAN_FAKE_FAILED);
                log.info("借款反欺诈回调,标的ID={},发送融360消息,消息内容={}", JSON.toJSONString(r360OrderStatusChangedDTO));
                rabbitTemplate.convertAndSend(RabbitConstant.Exchange.ASSET_EXCHANGE,
                        RabbitConstant.RoutingKey.ORDER_STATUS_NOFIFY_FOR_COMMON,
                        r360OrderStatusChangedDTO);
            }
            //标签系统-大数据流标事件
            activityEventService.sendMsgForFlowProject(project.getProjectId(), FlowProjectTypeEventEnum.BIGDATA_FLOW);
        }

        if (loanLocalService.isR360(project.getFromSource())) {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("projectId", project.getProjectId());
            jsonObject.put("result", loanAgainstFackRequestDTO.getModelResult());
            log.info("借款反欺诈回调,标的ID={},发送融360消息.", loanAgainstFackRequestDTO.getProjectId());
            rabbitTemplate.convertAndSend(RabbitConstant.Exchange.ASSET_R360_EXCHANGE, RabbitConstant.RoutingKey.STEP_LOAN_FRAUD_RESULT, jsonObject);
        }
    }

    @Override
    public void auditPointFakeCallback(AuditPointFackRequestDTO auditPointFackRequestDTO) {
        HintManager hintManager = HintManager.getInstance();
        hintManager.setMasterRouteOnly();
        log.info("天称审核要点反欺诈回调,标的ID={},内部方法auditPointFakeCallback请求参数={}.", auditPointFackRequestDTO.getProjectId(), JSON.toJSONString(auditPointFackRequestDTO));
        Integer auditResult = null;
        Project project = projectCommonService.queryProjectBasicInfo(auditPointFackRequestDTO.getProjectId());
        if (project == null) {
            log.error("天称审核要点反欺诈回调,标的ID={},发生异常,原因是标的不存在.", auditPointFackRequestDTO.getProjectId());
            return;
        }

        if (!(project.getStatus().equals(ProjectStatusEnum.CHECKING.getStatus()) && project.getSubStatus().equals(ProjectSubStatusEnum.CHECKING_LOAN_FAKE_PASS.getSubStatus()))) {
            log.error("天称审核要点反欺诈回调,标的ID={},发生异常,原因是标的不在审核中-借款反欺诈通过状态.", auditPointFackRequestDTO.getProjectId());
            return;
        }

        String autoAuditSwitchStatus = configLocalService.selectTradeConfigByEmun(TradeConfigEnum.AUTOAUDITSWITCHSTATUS);
        if(StringUtils.isEmpty(autoAuditSwitchStatus)){
            log.error("天称审核要点反欺诈回调,标的ID={},自动审核开关状态没配置,已按开启状态处理流程.", auditPointFackRequestDTO.getProjectId());
            autoAuditSwitchStatus="1";
        }

        if (auditPointFackRequestDTO.isRequestSuccess()) {
            //1.审核通过;
            if (auditPointFackRequestDTO.isAuditPass()) {
                auditResult = 1;

                log.info("天称审核要点反欺诈回调,标的ID={},审核通过.", auditPointFackRequestDTO.getProjectId());

                if(isAutoAuditSwitchStatusIsOpen(autoAuditSwitchStatus)){
                    log.info("天称审核要点反欺诈回调,标的ID={},自动审核开关开启,走自动审核通过流程.", auditPointFackRequestDTO.getProjectId());
                    //融360标的通过后为待确认状态
                    if (loanLocalService.isR360(project.getFromSource())) {
                        loanLocalService.updateProjectStatus(project.getProjectId(), null, ProjectSubStatusEnum.CHECKING_FOR_CONFIRM);
                    } else {
                        //上标至存管
                        PushProjectRequestDTO pushProjectRequestDTO = new PushProjectRequestDTO();
                        pushProjectRequestDTO.setAuditedTime(new Date());
                        pushProjectRequestDTO.setProjectId(project.getProjectId());
                        pushProjectRequestDTO.setPushFromEnum(PushProjectRequestDTO.PushFromEnum.AUTO_AUDIT);

                        pushProject(pushProjectRequestDTO);
                    }

                    loanLocalService.updateFinalAuditResult(project.getProjectId(),FinalAuditResultEnum.PASS);
                }else{
                    log.info("天称审核要点反欺诈回调,标的ID={},自动审核开关关闭,走人工审核流程.", auditPointFackRequestDTO.getProjectId());
                    auditResult = 3;
                    loanLocalService.toManualAudit(project);
                }
            }

            //2.审核拒绝
            else if (auditPointFackRequestDTO.isAuditReject()) {
                log.info("天称审核要点反欺诈回调,标的ID={},审核拒绝.", auditPointFackRequestDTO.getProjectId());
                auditResult = 2;
                ProjectFlowRequestDTO requestDTO = new ProjectFlowRequestDTO();
                requestDTO.setProjectId(project.getProjectId());
                requestDTO.setSubStatus(ProjectSubStatusEnum.MISCARRY_AUTO_FAILED);
                projectDubboService.flow(requestDTO);

                //2.2 通知用户借款审核不通过;
                //2.3 限制用户借款行为
                AddUserLoanLimitRequestDTO addUserLoanLimitRequestDTO = new AddUserLoanLimitRequestDTO();
                addUserLoanLimitRequestDTO.setLimitDays(Integer.valueOf(tradeConfigService.selectTradeConfigByEmun(TradeConfigEnum.SYSTEMAUTOREJECTLIMITDAYS)));
                addUserLoanLimitRequestDTO.setLimitType(LibraLoanLimitEnum.LOAN_AUDIT.getLimitType().intValue());
                addUserLoanLimitRequestDTO.setUserId(project.getBorrowUserId());
                addUserLoanLimitRequestDTO.setProductTypeEnum(ProductTypeEnum.SPEED_LOAN);

                userForbidDubboService.addUserLibraLoanLimit(addUserLoanLimitRequestDTO);

                loanLocalService.updateFinalAuditResult(project.getProjectId(),FinalAuditResultEnum.NO_PASS);

                //标签系统-审核模型流标事件
               activityEventService.sendMsgForFlowProject(project.getProjectId(), FlowProjectTypeEventEnum.AUDIT_MODEL_FLOW);
            }
            //3.需要人工审核
            else if (auditPointFackRequestDTO.isAuditReview()) {
                log.info("天称审核要点反欺诈回调,标的ID={},需要人工审核.", auditPointFackRequestDTO.getProjectId());
                auditResult = 3;
                loanLocalService.toManualAudit(project);
            }

            //1.2 更新标的自动审核结果;
            loanLocalService.updateAutoAuditResult4Project(auditPointFackRequestDTO.getProjectId(), auditResult.byteValue());
            //1.1 记录自动审核结果;
            loanLocalService.addAutoAuditResult(auditPointFackRequestDTO.getProjectId(), project.getBorrowUserId(), auditResult.byteValue(), auditPointFackRequestDTO.getAuditPoints());

            //1.3 若是融360标的,则需发送通知;
            if (loanLocalService.isR360(project.getFromSource())) {
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("userId", project.getBorrowUserId());
                jsonObject.put("projectId", project.getProjectId());
                jsonObject.put("auditResult", auditResult);
                jsonObject.put("autoAuditSwitchStatus", autoAuditSwitchStatus);
                log.info("天称审核要点反欺诈回调,标的ID={},发送融360通知,通知内容={}", auditPointFackRequestDTO.getProjectId(), JSON.toJSONString(jsonObject));
                rabbitTemplate.convertAndSend(RabbitConstant.Exchange.ASSET_EXCHANGE, RabbitConstant.RoutingKey.AUTO_AUDIT_MODEL_NOTIFY, jsonObject);
            }
        }
        //信审要点反欺诈请求失败;
        else {
            log.error("天称审核要点反欺诈回调,标的ID={},发生异常,原因是信审要点请求失败.", auditPointFackRequestDTO.getProjectId());
            loanLocalService.updateAutoAuditResult4Project(auditPointFackRequestDTO.getProjectId(), ProjectAuditResultEnum.TC_PARAM_ERROR.getCode());
        }
    }

    /**
     * 自动审核开关已开启?
     * @param autoAuditSwitchStatus
     * @return
     */
    private boolean isAutoAuditSwitchStatusIsOpen(String autoAuditSwitchStatus){
        return autoAuditSwitchStatus.equals("1");
    }

    @Override
    public void riskRuleCallback(RiskRuleRequestDTO riskRuleRequestDTO) {
        HintManager hintManager = HintManager.getInstance();
        hintManager.setMasterRouteOnly();
        //1.保存风险规则信息;
        if (riskRuleRequestDTO.getVarsResult() != null && riskRuleRequestDTO.getVarsResult().size() > 0) {
            ProjectRiskInfoSaveByDetailJSONDTO projectRiskInfoSaveByDetailJSONDTO = new ProjectRiskInfoSaveByDetailJSONDTO();
            projectRiskInfoSaveByDetailJSONDTO.setProjectId(riskRuleRequestDTO.getProjectId());
            projectRiskInfoSaveByDetailJSONDTO.setUserId(riskRuleRequestDTO.getUserId());
            List<ProjectRiskInfoSaveByDetailJSONDTO.VarResult> varResults = new ArrayList<ProjectRiskInfoSaveByDetailJSONDTO.VarResult>();
            for (RiskRuleRequestDTO.VarResult varResult : riskRuleRequestDTO.getVarsResult()) {
                ProjectRiskInfoSaveByDetailJSONDTO.VarResult varResult4Save = new ProjectRiskInfoSaveByDetailJSONDTO.VarResult();
                varResult4Save.setCode(varResult.getCode());
                varResult4Save.setCodeDesc(varResult.getCodeDesc());
                varResult4Save.setComputeEndTime(varResult.getComputeEndTime());
                varResult4Save.setComputeStartTime(varResult.getComputeStartTime());
                varResult4Save.setDetailJson(varResult.getDetailJson());
                varResult4Save.setVarId(varResult.getVarId());
                varResult4Save.setVarNameCn(varResult.getVarNameCn());
                varResult4Save.setVarNameEn(varResult.getVarNameEn());
                varResult4Save.setVarQueryType(varResult.getVarQueryType());

                varResults.add(varResult4Save);
            }
            projectRiskInfoSaveByDetailJSONDTO.setVarsResult(varResults);

            log.info("风险规则回调,标的ID={},调用保存风险规则信息接口,请求参数={}", riskRuleRequestDTO.getProjectId(), projectRiskInfoSaveByDetailJSONDTO);
            projectRiskInfoDubboService.saveByDetailJson(projectRiskInfoSaveByDetailJSONDTO);

            CallRiskRuleRecord callRiskRuleRecord2Update=new CallRiskRuleRecord();
            callRiskRuleRecord2Update.setProjectId(riskRuleRequestDTO.getProjectId());
            callRiskRuleRecord2Update.setUpdateTime(new Date());
            callRiskRuleRecord2Update.setRiskRuleCallBackStatus((byte)1);

            loanLocalService.updateCallRiskRuleRecord(callRiskRuleRecord2Update);
//            loanLocalService.deleteCallRiskRuleRecord(riskRuleRequestDTO.getProjectId());
//            log.info("风险规则回调,标的ID={},已删除调用风险规则信息接口记录", riskRuleRequestDTO.getProjectId(), projectRiskInfoSaveByDetailJSONDTO);
        } else {
            log.error("风险规则回调,标的ID={},发生异常,原因是变量结果集合为空.", riskRuleRequestDTO.getProjectId());
            throw new RuntimeException("变量结果集合为空.");
        }
    }

    private List<RelationVerification> transRelationVerificationDTO(List<RelationVerificationDTO> relationVerificationDTOList){
        List<RelationVerification> relationVerificationList=new ArrayList<RelationVerification>();
        for(RelationVerificationDTO relationVerificationDTO:relationVerificationDTOList){
            RelationVerification relationVerification=new RelationVerification();
            relationVerification.setRelation_phone(privacyMasks.decryptPrivacy(relationVerificationDTO.getRelation_phone()));
            relationVerification.setRelation_name(relationVerificationDTO.getRelation_name());

            relationVerificationList.add(relationVerification);
        }

        return relationVerificationList;
    }

    /**
     * 学历层次：1-中专及以下，2-大专，3-本科，4-硕士，5-博士及以上
     * @param educationLevel
     * @return
     */
    private String transEducationLevel2Degree(Byte educationLevel) {
        switch (educationLevel) {
            case 1:
                return DegreeEnum.POLYTECHNIC.getType();
            case 2:
                return DegreeEnum.JUNIOR_COLLEGE.getType();
            case 3:
                return DegreeEnum.REGULAR_COLLEGE.getType();
            case 4:
                return DegreeEnum.MASTER.getType();
            case 5:
                return DegreeEnum.DOCTOR.getType();
            default:
                return null;
        }
    }

    //退回标的基本信息检查
    private SpeedLoanApplyCommonResponseDTO speedLoanApplyForBackCheck(SpeedLoanApplyForBackRequestDTO requestDTO) {
        SpeedLoanApplyCommonResponseDTO responseDTO = new SpeedLoanApplyCommonResponseDTO();

        //检查是否允许发布借款
        SpeedLoanPublishCheckRequestDTO speedLoanPublishCheckRequestDTO = new SpeedLoanPublishCheckRequestDTO();
        try {
            BeanUtils.copyProperties(requestDTO, speedLoanPublishCheckRequestDTO);
            //退回标的基本信息检查
            this.backProjectBasicInfoCheck(requestDTO, responseDTO);

            speedLoanPublishCheckRequestDTO.setProjectApplyCheckTypeEnum(ProjectApplyCheckTypeEnum.BACK_PROJ_SUBMIT_CHECK);
            this.speedLoanPublishCheckMain(speedLoanPublishCheckRequestDTO, responseDTO);

            //借款配置数据检查
            this.speedLoanConfigurationCheck(requestDTO, responseDTO);
            responseDTO.setStatus(LoanLimitMessageEnum.LIMIT_10000.getLimitCode());
        } catch (BizException bizException) {
            log.error("speedLoanApplyForBackCheck error", bizException);
            this.backProjectDetailUrlDeal(bizException,speedLoanPublishCheckRequestDTO,responseDTO);
        }

        return responseDTO;
    }

    //退回标的基本信息检查
    private void backProjectBasicInfoCheck(SpeedLoanApplyForBackRequestDTO requestDTO, SpeedLoanApplyCommonResponseDTO responseDTO) {
        loanLocalService.backProjectBasicInfoCheck(requestDTO, responseDTO);
    }

    @Override
    public ContractPreviewInfoDTO getContractPreviewInfo(String userId) throws ExecutionException, InterruptedException {
        ContractPreviewInfoDTO contractPreviewInfo = new ContractPreviewInfoDTO();
        contractPreviewInfo.setUserId(userId);

        Future<UserContractInfoDTO> userContractInfoFuture = RpcContext.getContext().asyncCall(() -> userBasicInfoDubboService.getUserContractInfo(userId));
        Future<List<SimpleOrgInfo>> simpleOrgInfoListFuture = RpcContext.getContext().asyncCall(() -> organizationDubboService.getGuaranteeAndAntiGuaranteeInfo());
        Future<GetSimpleUserBankCardResponse> userBankCardResponseFuture = RpcContext.getContext().asyncCall(() -> bankCardDubboService.getSimpleUserBankCard(userId));
        // 用户联系信息
        UserContractInfoDTO userContractInfo = userContractInfoFuture.get();
        if(userContractInfo != null) {
            contractPreviewInfo.setMobileNo(userContractInfo.getMobileNo());
            contractPreviewInfo.setEmail(userContractInfo.getEmail());
            contractPreviewInfo.setAddress(userContractInfo.getLivingAdress());
        }
        // 用户银行卡信息
        GetSimpleUserBankCardResponse userBankCardResponse = userBankCardResponseFuture.get();
        if(userBankCardResponse != null && userBankCardResponse.getBankCard() != null) {
            contractPreviewInfo.setBankAccount(userBankCardResponse.getBankCard().getBankAccountNo());
            contractPreviewInfo.setBankMobileNo(userBankCardResponse.getBankCard().getMobileNo());
            contractPreviewInfo.setBankName(userBankCardResponse.getBankCard().getBankType().getBankName());
            contractPreviewInfo.setLoanName(userBankCardResponse.getBankCard().getRealName());
            contractPreviewInfo.setIdCard(userBankCardResponse.getBankCard().getIdCard());
        }
        // 担保、反担保信息
        List<SimpleOrgInfo> simpleOrgInfoList = simpleOrgInfoListFuture.get();
        for(SimpleOrgInfo simpleOrgInfo : simpleOrgInfoList) {
            if(OrganizationEnum.GUARANTEE_ORG.getType().equals(simpleOrgInfo.getType())) {
                contractPreviewInfo.setGuarantee(simpleOrgInfo.getOrganizationName());
                contractPreviewInfo.setGuaranteeCorporation(simpleOrgInfo.getRepresentative());
                contractPreviewInfo.setGuaranteeMobileNo(simpleOrgInfo.getLegalPersonPhoneNumber());
                contractPreviewInfo.setGuaranteeAddress(simpleOrgInfo.getAddress());
                contractPreviewInfo.setGuaranteeEmail(simpleOrgInfo.getEmail());
                contractPreviewInfo.setGuaranteeCreditCode(simpleOrgInfo.getInstitutionalReditCode());
            }else if(OrganizationEnum.ANTI_GUARANTEE_ORG.getType().equals(simpleOrgInfo.getType())) {
                contractPreviewInfo.setCounterGuarantee(simpleOrgInfo.getOrganizationName());
                contractPreviewInfo.setCounterGuaranteeCorporation(simpleOrgInfo.getRepresentative());
                contractPreviewInfo.setCounterGuaranteeMobileNo(simpleOrgInfo.getLegalPersonPhoneNumber());
                contractPreviewInfo.setCounterGuaranteeAddress(simpleOrgInfo.getAddress());
                contractPreviewInfo.setCounterGuaranteeEmail(simpleOrgInfo.getEmail());
                contractPreviewInfo.setCounterGuaranteeCreditCode(simpleOrgInfo.getInstitutionalReditCode());
            }
        }
        return contractPreviewInfo;
    }

    /**
     * 扫描可推送标的,并推送至存管;
     */
    @Override
    public void pushProjectScan() {
        String batchNo = DateUtils.getCurrDatetime();
        List<ProjectPushRecord> pushProjectList = loanLocalService.selectPushProjectList();
        log.info("推送标的扫描,批次号={},扫描结果={}", batchNo, JSONObject.toJSONString(pushProjectList));
        if (pushProjectList != null && pushProjectList.size() > 0) {
            for (ProjectPushRecord projectPushRecord : pushProjectList) {
                try {
                    ProjectPushRecord pushProjectSuccessRecord = loanLocalService.selectPushProjectSuccessRecordByProjectId(Long.valueOf(projectPushRecord.getProjectNo()));
                    if (pushProjectSuccessRecord != null) {
                        log.error("推送标的扫描,批次号={},标的ID={},发生异常,原因是该标的已上过标.", batchNo, projectPushRecord.getProjectNo());
                        continue;
                    }

                    Project project = projectCommonService.queryProjectBasicInfo(Long.valueOf(projectPushRecord.getProjectNo()));
                    if (project == null) {
                        log.error("推送标的扫描,批次号={},标的ID={},发生异常,原因是标的不存在.", batchNo, Long.valueOf(projectPushRecord.getProjectNo()));
                        continue;
                    }

                    //上标至存管
                    PushProjectRequestDTO pushProjectRequestDTO =new PushProjectRequestDTO();
                    pushProjectRequestDTO.setAuditedTime(projectPushRecord.getAuditedTime());
                    pushProjectRequestDTO.setAuditManagerId(projectPushRecord.getAuditManagerId());
                    pushProjectRequestDTO.setAuditManagerName(projectPushRecord.getAuditManagerName());
                    pushProjectRequestDTO.setProjectId(project.getProjectId());
                    pushProjectRequestDTO.setPushFromEnum(PushProjectRequestDTO.PushFromEnum.getEnumByType(projectPushRecord.getPushFrom()));
                    pushProjectRequestDTO.setProjectPushRecordId(projectPushRecord.getId());
                    pushProject(pushProjectRequestDTO);    //极速贷标准内核逻辑
                    log.info("推送标的扫描,批次号={},标的ID={},处理结束.", batchNo, projectPushRecord.getProjectNo());
                } catch (Exception e) {
                    log.error("推送标的扫描,批次号={},标的ID={},发生系统异常,原因={}", batchNo, projectPushRecord.getProjectNo(), e);
                    continue;
                }
            }
        }
    }

    @Override
    public void addressList4RiskRuleScan() {
        String batchNo = DateUtils.getCurrDatetime();
        //1.查询通录讯回流成功的标的;
        List<CallRiskRuleRecord> waitRiskRuleRecordList=loanLocalService.searchWaitRiskRuleRecordList();
        log.info("发起天称十二项风险规则扫描,批次号={},扫描结果={}", batchNo, JSONObject.toJSONString(waitRiskRuleRecordList));
        if (waitRiskRuleRecordList != null && waitRiskRuleRecordList.size() > 0) {
            for (CallRiskRuleRecord callRiskRuleRecord : waitRiskRuleRecordList) {
                try {
                    Project project=projectCommonService.queryProjectBasicInfo(callRiskRuleRecord.getProjectId());
                    if(project==null){
                        log.error("发起天称十二项风险规则扫描,批次号={},标的ID={},发生异常,原因是标的不存在.", batchNo, callRiskRuleRecord.getProjectId());
                        loanLocalService.deleteCallRiskRuleRecord(callRiskRuleRecord.getProjectId());
                        continue;
                    }

                    if(!(project.getStatus().equals(ProjectStatusEnum.CHECKING.getStatus())
                            &&project.getSubStatus().equals(ProjectSubStatusEnum.CHECKING_INIT.getSubStatus()))){
                        loanLocalService.deleteCallRiskRuleRecord(callRiskRuleRecord.getProjectId());
                        log.info("发起天称十二项风险规则扫描,批次号={},标的ID={},标的状态不在审核中,标的状态={},标的子状态={},已清除风险规则调用记录数据.", batchNo, callRiskRuleRecord.getProjectId(),project.getStatus(),project.getSubStatus());
                        continue;
                    }

                    log.info("发起天称十二项风险规则扫描,批次号={},标的ID={}", batchNo, callRiskRuleRecord.getProjectId());
                    SpeedLoanUserDataStatusDTO speedLoanUserDataStatusDTO = getSpeedLoanUserDataStatus(project.getBorrowUserId());
                    if (speedLoanUserDataStatusDTO == null) {
                        log.error("发起天称十二项风险规则扫描,批次号={},标的ID={},发生异常,原因是用户认证信息不存在.", batchNo, callRiskRuleRecord.getProjectId());
                        continue;
                    }

                    //运营商已授权并且已成功获取数据;
                    if (!loanLocalService.isR360(project.getFromSource())) {
                        if (!speedLoanUserDataStatusDTO.getTelOperatorAuthStatus().equals(UserDataStatusEnum.PhoneOperatorStatus.GETSUCCESS.getStatus())) {
                            log.error("发起天称十二项风险规则扫描,批次号={},标的ID={},发生异常,原因是用户运营商未授权并获取数据.", batchNo, callRiskRuleRecord.getProjectId());
                            continue;
                        }
                    }

                    UserIdentityInfoResponseDTO userIdentityInfoResponseDTO = userIdentityDubboService.get(project.getBorrowUserId());
                    if (userIdentityInfoResponseDTO == null) {
                        log.error("发起天称十二项风险规则扫描,批次号={},标的ID={},发生异常,原因是用户身份信息不存在.", batchNo, project.getProjectId());
                        continue;
                    }

                    UserBasicInfoResponseDTO userBasicInfoResponseDTO = userBasicInfoDubboService.getUserBasicInfo(project.getBorrowUserId());
                    if (userBasicInfoResponseDTO == null) {
                        log.error("发起天称十二项风险规则扫描,批次号={},标的ID={},发生异常,原因是用户基础信息不存在.", batchNo, project.getProjectId());
                        continue;
                    }

                    //触发天称12项风险规则;
                    RiskRuleRequest riskRuleRequest = new RiskRuleRequest();
                    riskRuleRequest.setTransDate(new Date());
                    riskRuleRequest.setTransNo(String.valueOf(snowflakeIdWorker.nextId()));
                    riskRuleRequest.setProjectId(String.valueOf(project.getProjectId()));
                    riskRuleRequest.setUserId(String.valueOf(project.getBorrowUserId()));
                    riskRuleRequest.setIdentityCard(userIdentityInfoResponseDTO.getIdCard());
                    riskRuleRequest.setMobilePhone(userBasicInfoResponseDTO.getMobileNo());
                    riskRuleRequest.setRealName(userIdentityInfoResponseDTO.getName());
                    riskRuleRequest.setFunctionCode(FunctionCodeEnum.LIBRA_FRAUD_RISK_RULE.getBodyFunctionCode());
                    riskRuleRequest.setIsNeedDetail(IsNeedDetailEnum.TRUE.getType());
                    riskRuleRequest.setCallUrl(String.format(TIANCHENG_AFP_CALLBACK_URL, webHttpUrl, TianchengCallbackEventEnum.SPEED_LOAN_RISK_RULE.getEventType(), project.getProjectId(), FunctionCodeEnum.LIBRA_FRAUD_RISK_RULE.getBodyFunctionCode()));
                    riskRuleRequest.setVars(getVars());
//                    riskRuleRequest.setFromSource(project.getFromSource()==null?"1":project.getFromSource().equals(ProjectFromSourceEnum.R360.getType())?"2":"1");
                    ProjectFromSourceEnum projectFromSourceEnum = ProjectFromSourceEnum.enumOf(project.getFromSource());
                    riskRuleRequest.setFromSource(TCRiskRuleFromSourceEnum.getTCRiskRuleFromSourceByProjectFromSource(projectFromSourceEnum));

                    log.info("发起天称十二项风险规则扫描,批次号={},标的ID={},请求天称风险规则请求参数={}", batchNo, project.getProjectId(), riskRuleRequest);
                    Date now = new Date();
                    try {
                        CompletableFuture<RiskRuleResponse> result = tianchengAgainstFakeService.riskRule(riskRuleRequest);
                        RiskRuleResponse riskRuleResponse = result.get();
                        log.info("发起天称十二项风险规则扫描,批次号={},标的ID={},请求天称风险规则响应结果={}", batchNo, project.getProjectId(), riskRuleResponse);

                        CallRiskRuleRecord callRiskRuleRecord2Update = new CallRiskRuleRecord();
                        callRiskRuleRecord2Update.setProjectId(project.getProjectId());
                        callRiskRuleRecord2Update.setCallRiskRuleTime(now);
                        callRiskRuleRecord2Update.setCallTimes((byte) ((callRiskRuleRecord.getCallTimes() == null ? 0 : callRiskRuleRecord.getCallTimes().intValue()) + 1));
                        callRiskRuleRecord2Update.setUpdateTime(now);

                        loanLocalService.updateCallRiskRuleRecord(callRiskRuleRecord2Update);
                        log.info("发起天称十二项风险规则扫描,批次号={},标的ID={},已录入风险规则请求记录表", batchNo, project.getProjectId());
                    } catch (Exception e) {
                        log.error("发起天称十二项风险规则扫描,批次号={},标的ID={},发生异常,错误信息={}", batchNo, project.getProjectId(), e);
                        CallRiskRuleRecord callRiskRuleRecord2Update = new CallRiskRuleRecord();
                        callRiskRuleRecord2Update.setProjectId(project.getProjectId());
                        callRiskRuleRecord2Update.setCallRiskRuleTime(now);
                        callRiskRuleRecord2Update.setCallTimes((byte)((callRiskRuleRecord.getCallTimes()==null?0:callRiskRuleRecord.getCallTimes().intValue())+1));
                        callRiskRuleRecord2Update.setUpdateTime(now);

                        loanLocalService.updateCallRiskRuleRecord(callRiskRuleRecord2Update);
                        continue;
                    }

                } catch (Exception e) {
                    log.error("发起天称十二项风险规则扫描,批次号={},标的ID={},发生系统异常,错误信息={}", batchNo, callRiskRuleRecord.getProjectId(), e);
                    continue;
                }
            }
        }
    }

    @Override
    public void addressListTimeoutScan(){
        String batchNo = DateUtils.getCurrDatetime();
        List<AddressListWaitBackflowRecord> addressListWaitBackflowRecordList = loanLocalService.searchAddressListTimeoutRecordList();
        log.info("通讯录回流超时退回扫描,批次号={},扫描结果={}.", batchNo,JSONObject.toJSONString(addressListWaitBackflowRecordList));
        if (addressListWaitBackflowRecordList != null && addressListWaitBackflowRecordList.size() > 0) {
            for (AddressListWaitBackflowRecord addressListWaitBackflowRecord : addressListWaitBackflowRecordList) {
                try {
                    log.info("通讯录回流超时退回扫描,批次号={},标的ID={}.处理开始", batchNo, addressListWaitBackflowRecord.getProjectId());
                    loanLocalService.returnProject(addressListWaitBackflowRecord.getProjectId(), ProjectSubStatusEnum.CHECKING_BACK_ADDRESSLIST_TIMEOUT);
                    loanLocalService.deleteAddressListBackflowByProjectId(addressListWaitBackflowRecord.getProjectId());
                    log.info("通讯录回流超时退回扫描,批次号={},标的ID={}.处理结束", batchNo, addressListWaitBackflowRecord.getProjectId());
                }catch(Exception e ){
                    log.error("通讯录回流超时退回扫描,批次号={},标的ID={}.发生系统异常,错误信息={}", batchNo, addressListWaitBackflowRecord.getProjectId(),e);
                }
            }
        }
    }

    @Override
    public void addressListScan() {
        String batchNo = DateUtils.getCurrDatetime();
        List<AddressListWaitBackflowRecord> addressListWaitBackflowRecordList = loanLocalService.searchAddressListWaitBackflowRecordList();
        log.info("通讯录待回流扫描,批次号={},扫描结果={}", batchNo, addressListWaitBackflowRecordList == null ? 0 : addressListWaitBackflowRecordList.size());
        if (addressListWaitBackflowRecordList != null && addressListWaitBackflowRecordList.size() > 0) {
            for (AddressListWaitBackflowRecord addressListWaitBackflowRecord : addressListWaitBackflowRecordList) {
                try {
                    log.info("通讯录待回流扫描,批次号={},标的ID={}.", batchNo, addressListWaitBackflowRecord.getProjectId());
                    if (StringUtils.isEmpty(addressListWaitBackflowRecord.getDeviceId())) {
                        log.error("通讯录待回流扫描,批次号={},标的ID={},发生异常,原因是设备id为空.", batchNo, addressListWaitBackflowRecord.getProjectId());
                        continue;
                    }

                    if (StringUtils.isEmpty(addressListWaitBackflowRecord.getAddressList())) {
                        log.error("通讯录待回流扫描,批次号={},标的ID={},发生异常,原因是通讯录详情为空.", batchNo, addressListWaitBackflowRecord.getProjectId());
                        continue;
                    }

                    byte[] bytes = org.apache.commons.codec.binary.Base64.decodeBase64(addressListWaitBackflowRecord.getAddressList());
                    String addresslistStr = new String(bytes);
                    if (StringUtils.isEmpty(addresslistStr)) {
                        log.error("通讯录待回流扫描,批次号={},标的ID={},发生异常,原因是通讯录详情转Base64后为空.", batchNo, addressListWaitBackflowRecord.getProjectId());
                        continue;
                    }

                    AddressListRequest addressListRequest = new AddressListRequest();
                    addressListRequest.setAddressList(JSON.parseArray(addresslistStr).toJavaList(AddressListRequest.Address.class));
                    addressListRequest.setEquipmentStatus(addressListWaitBackflowRecord.getEquipmentStatus().intValue());
                    addressListRequest.setOs(addressListWaitBackflowRecord.getOs());
                    addressListRequest.setDeviceId(addressListWaitBackflowRecord.getDeviceId());
                    addressListRequest.setUserId(String.valueOf(addressListWaitBackflowRecord.getUserId()));

                    try {
                        log.info("通讯录待回流扫描,批次号={},标的ID={},发起天称通讯录回流请求,请求参数={}", batchNo, addressListWaitBackflowRecord.getProjectId(), addressListRequest);
                        CompletableFuture<TianchengUapResponse> completableFuture = tianchengDataFlowService.addressList(addressListRequest);
                        TianchengUapResponse tianchengUapResponse = completableFuture.get();
                        log.info("通讯录待回流扫描,批次号={},标的ID={},发起天称通讯录回流请求,响应结果={}", batchNo, addressListWaitBackflowRecord.getProjectId(), tianchengUapResponse);
                        //回流成功
                        if (tianchengUapResponse.isSuccess()) {
                            loanLocalService.addressListBackflowSuccess(addressListWaitBackflowRecord);
                        }
                    } catch (Exception e) {
                        log.error("通讯录待回流扫描,批次号={},标的ID={},发生系统异常,错误信息={}.", batchNo, addressListWaitBackflowRecord.getProjectId(), e);
                        continue;
                    }
                } catch (Exception e) {
                    log.error("通讯录待回流扫描,批次号={},标的ID={},发生系统异常,错误信息={}.", batchNo, addressListWaitBackflowRecord.getProjectId(), e);
                    continue;
                }
            }
        }
    }

    @Override
    public void riskRuleTimeoutScan() {
        String batchNo = DateUtils.getCurrDatetime();
        List<CallRiskRuleRecord> timeOutRecordList = loanLocalService.searchTimeOutRecordList();
        log.info("风险规则超时回调扫描,批次号={},扫描结果={}", batchNo, JSONObject.toJSONString(timeOutRecordList));
        if (timeOutRecordList != null && timeOutRecordList.size() > 0) {
            for (CallRiskRuleRecord callRiskRuleRecord : timeOutRecordList) {
                try {
                    //1.发起天称借款反欺诈;
                    Project project = projectCommonService.queryProjectBasicInfo(callRiskRuleRecord.getProjectId());
                    if (project == null) {
                        log.error("风险规则超时回调扫描,批次号={},标的ID={},发生异常,原因是标的不存在.", batchNo, callRiskRuleRecord.getProjectId());
                        continue;
                    }

                    //借款人手动取消、管理人员手动取消、管理员批量流标，这些标的可能由于来不及走流程就被流标了，因上，不再需要走流程，直接清除处理;
                    if (project.getSubStatus().equals(ProjectSubStatusEnum.MISCARRY_CANCER_BORROWER.getSubStatus())
                            || project.getSubStatus().equals(ProjectSubStatusEnum.MISCARRY_CANCER_MANAGER.getSubStatus())
                            || project.getSubStatus().equals(ProjectSubStatusEnum.MISCARRY_LOAN_BATCH.getSubStatus())) {
                        log.info("风险规则超时回调扫描,批次号={},标的ID={},由于标的已流标，已清除风险规则调用记录。", batchNo, callRiskRuleRecord.getProjectId());
                        loanLocalService.deleteCallRiskRuleRecord(project.getProjectId());
                        continue;
                    }

                    UserBasicInfoResponseDTO userBasicInfoResponseDTO = userBasicInfoDubboService.getUserBasicInfo(project.getBorrowUserId());
                    if (userBasicInfoResponseDTO == null) {
                        log.error("风险规则超时回调扫描,批次号={},标的ID={},发生异常,原因是用户基础信息不存在.", batchNo, project.getProjectId());
                        continue;
                    }
                    UserIdentityInfoResponseDTO userIdentityInfoResponseDTO = userIdentityDubboService.get(project.getBorrowUserId());
                    if (userIdentityInfoResponseDTO == null) {
                        log.error("风险规则超时回调扫描,批次号={},标的ID={},发生异常,原因是用户身份信息不存在.", batchNo, project.getProjectId());
                        continue;
                    }
                    UserPersonalDubboInfoDTO userPersonalDubboInfoDTO = userDataDubboService.userPersonalInfo(project.getBorrowUserId());
                    if (userPersonalDubboInfoDTO == null) {
                        log.error("风险规则超时回调扫描,批次号={},标的ID={},发生异常,原因是用户个人信息不存在.", batchNo, project.getProjectId());
                        continue;
                    }

                    BankCardDTO bankCardDTO = bankCardDubboService.getUserBankCardInfo(project.getBorrowUserId());
                    if (bankCardDTO == null) {
                        log.error("风险规则超时回调扫描,批次号={},标的ID={},发生异常,原因是用户当前未绑定银行卡.", batchNo, project.getProjectId());
                        continue;
                    }

                    ProjectLoanDeviceInfo projectLoanDeviceInfo = loanLocalService.queryLoanDeviceInfoByProjectId(project.getProjectId());
                    if (projectLoanDeviceInfo == null) {
                        log.error("风险规则超时回调扫描,批次号={},标的ID={},发生异常,原因是用户借款设备信息不存在.", batchNo, project.getProjectId());
                        continue;
                    }
                    UserExtInfoResponseDTO userExtInfoResponseDTO = userExtInfoDubboService.queryUserBasicInfo(project.getBorrowUserId());

                    PublishLoanAgainsFakeRequest publishLoanAgainsFakeRequest = new PublishLoanAgainsFakeRequest();
                    publishLoanAgainsFakeRequest.setUser_id(String.valueOf(project.getBorrowUserId()));
                    publishLoanAgainsFakeRequest.setTransNo(String.valueOf(snowflakeIdWorker.nextId()));
                    publishLoanAgainsFakeRequest.setProjectId(String.valueOf(project.getProjectId()));
                    publishLoanAgainsFakeRequest.setIdentity_card(userIdentityInfoResponseDTO.getIdCard());
                    publishLoanAgainsFakeRequest.setIdtype("0");
                    publishLoanAgainsFakeRequest.setName(userIdentityInfoResponseDTO.getName());
                    publishLoanAgainsFakeRequest.setBank_cart(bankCardDTO.getBankAccountNo());
                    publishLoanAgainsFakeRequest.setReasonno("01");
                    publishLoanAgainsFakeRequest.setAccount_login(userBasicInfoResponseDTO.getNickName());
                    publishLoanAgainsFakeRequest.setMobile_phone(userBasicInfoResponseDTO.getMobileNo());
                    publishLoanAgainsFakeRequest.setLoanType("01");
                    publishLoanAgainsFakeRequest.setCurrency("1");
                    publishLoanAgainsFakeRequest.setLoanMoney(project.getContractAmount().floatValue());
                    publishLoanAgainsFakeRequest.setLoanTimeLimit(project.getDeadline());
                    publishLoanAgainsFakeRequest.setApplyDate(DateFormatUtils.format(project.getCreateTime(), "yyyy/MM/dd"));
                    publishLoanAgainsFakeRequest.setAssureType("D");
                    publishLoanAgainsFakeRequest.setCreditAddress(StringUtils.isEmpty(project.getLoanProvince()) ? "" : project.getLoanProvince()
                            .concat(StringUtils.isEmpty(project.getLoanCity()) ? "" : project.getLoanCity())
                            .concat(StringUtils.isEmpty(project.getLoanArea()) ? "" : project.getLoanArea()));
                    publishLoanAgainsFakeRequest.setIs_student(0);
                    publishLoanAgainsFakeRequest.setIs_first_time(loanLocalService.isFirstTime4TianCheng(project.getBorrowUserId()));
                    publishLoanAgainsFakeRequest.setDegree(String.valueOf(userPersonalDubboInfoDTO.getEducationLevel()));
                    publishLoanAgainsFakeRequest.setSource(0);
                    publishLoanAgainsFakeRequest.setExt_cutype(userCreditService.isOldUser(userBasicInfoResponseDTO.getUserId()));
                    publishLoanAgainsFakeRequest.setPlatform_type(project.getFromSource().equals(ProjectFromSourceEnum.R360.getType()) ? PlatformTypeEnum.R360.getType() : PlatformTypeEnum.NIIWOO.getType());
                    publishLoanAgainsFakeRequest.setRequest_type("1");
                    publishLoanAgainsFakeRequest.setNotify_server_url(String.format(TIANCHENG_AFP_CALLBACK_URL, webHttpUrl, TianchengCallbackEventEnum.SPEED_LOAN_FAKE_FRAUD.getEventType(), project.getProjectId(), FunctionCodeEnum.LIBRA_FRAUD_PUBLISH_SPEED_LOAN.getHeadFunctionCode()));
                    publishLoanAgainsFakeRequest.setTransDate(new Date());
                    publishLoanAgainsFakeRequest.setDevice_type(PublishLoanAgainsFakeRequest.DeviceTypeEnum.getTypeByOs(projectLoanDeviceInfo.getOs()));
                    publishLoanAgainsFakeRequest.setBlackBox(userExtInfoResponseDTO == null ? null : userExtInfoResponseDTO.getBlackBox());

                    AgainstFakeResponse againstFakeResponse = null;
                    try {
                        if (loanLocalService.isNoZhiMa()) {
                            log.info("风险规则超时回调扫描,批次号={},标的ID={},发起借款反欺诈去芝麻模型请求参数={}.", batchNo, project.getProjectId(), publishLoanAgainsFakeRequest);
                            CompletableFuture<AgainstFakeResponse> completableFuture = tianchengAgainstFakeService.publishLoanAgainsFakeNoZHIMA(publishLoanAgainsFakeRequest);
                            againstFakeResponse = completableFuture.get();
                            log.info("风险规则超时回调扫描,批次号={},标的ID={},发起借款反欺诈去芝麻模型请求,同步响应结果={}.", batchNo, project.getProjectId(), againstFakeResponse);
                        } else {
                            log.info("风险规则超时回调扫描,批次号={},标的ID={},发起借款反欺诈请求参数={}.", batchNo, project.getProjectId(), publishLoanAgainsFakeRequest);
                            CompletableFuture<AgainstFakeResponse> completableFuture = tianchengAgainstFakeService.publishLoanAgainsFake(publishLoanAgainsFakeRequest);
                            againstFakeResponse = completableFuture.get();
                            log.info("风险规则超时回调扫描,批次号={},标的ID={},发起借款反欺诈请求,同步响应结果={}.", batchNo, project.getProjectId(), againstFakeResponse);
                        }

                        //如果天称接收成功,则更新借款反欺诈发起状态为已发起;
                        if (againstFakeResponse != null && againstFakeResponse.getStatus().equals("0")) {
                            loanLocalService.updateCallRiskRuleRecordLoanFakeStatus(project.getProjectId(), (byte) 1);
                            log.info("风险规则超时回调扫描,批次号={},标的ID={},已更新t_tc_call_risk_rule_record反欺诈状态为成功.", batchNo, project.getProjectId());
                        }
                        //否则更新为失败;
                        else {
                            loanLocalService.updateCallRiskRuleRecordLoanFakeStatus(project.getProjectId(), (byte) 2);
                            log.info("风险规则超时回调扫描,批次号={},标的ID={},已更新t_tc_call_risk_rule_record反欺诈状态为失败.", batchNo, project.getProjectId());
                        }

                        try {
                            RpcContext.getContext().asyncCall(() -> tianChengDataAnalysisDubboService.contactsAnalysis(project.getBorrowUserId().toString(), userBasicInfoResponseDTO.getMobileNo(), project.getFromSource() == null ? "1" : project.getFromSource().equals(ProjectFromSourceEnum.R360.getType()) ? "2" : "1"));
                            RpcContext.getContext().asyncCall(() -> tianChengDataAnalysisDubboService.callRecordAnalysis(project.getBorrowUserId().toString(), userBasicInfoResponseDTO.getMobileNo(), project.getFromSource() == null ? "1" : project.getFromSource().equals(ProjectFromSourceEnum.R360.getType()) ? "2" : "1"));
                            RpcContext.getContext().asyncCall(() -> tianChengDataAnalysisDubboService.smsAnalysis(project.getBorrowUserId().toString(), userBasicInfoResponseDTO.getMobileNo()));
                        } catch (Exception e) {
                            log.error("风险规则超时回调扫描,批次号={},标的ID={},发起天称分析接口时发生系统异常,错误信息={}", batchNo, project.getProjectId(), e);
                        }
                    } catch (Exception e) {
                        log.error("风险规则超时回调扫描,批次号={},标的ID={},发生系统异常,错误信息={}", batchNo, project.getProjectId(), e);
                        continue;
                    }
                    log.info("风险规则超时回调扫描,批次号={},标的ID={},发起借款反欺诈请求,操作完成", batchNo, project.getProjectId());
                } catch (Exception e) {
                    log.error("风险规则超时回调扫描,批次号={},标的ID={},发生系统异常,错误信息={}", batchNo, callRiskRuleRecord.getProjectId(), e);
                    continue;
                }
            }
        }
    }

    @Override
    public void cleanCallRiskRuleRecord() {
        String batchNo = DateUtils.getCurrDatetime();
        log.info("开始清理风险规则调用记录,批次号={}", batchNo);
        int cleanCount=loanLocalService.cleanCallRiskRuleRecord();
        log.info("结束清理风险规则调用记录,批次号={},本次清理条数={}", batchNo,cleanCount);
    }

    @Override
    public void flowReturnTimeoutProjectScan() {
        String batchNo = DateUtils.getCurrDatetime();
        String timeoutDeadline=tradeConfigService.selectTradeConfigByEmun(TradeConfigEnum.FASTLOAN_SURETIME);
        if(StringUtils.isEmpty(timeoutDeadline)){
            log.info("退回超时流标扫描,批次号={},借款人确认最长期限(天)未配置.", batchNo);
            return;
        }
        List<Project> timeOutRecordList = loanLocalService.selectReturnTimeoutProject(Integer.valueOf(timeoutDeadline));
        log.info("退回超时流标扫描,批次号={},扫描结果={}", batchNo, JSONObject.toJSONString(timeOutRecordList));
        if (timeOutRecordList == null || timeOutRecordList.size() == 0) {
            log.info("退回超时流标扫描,批次号={},本次扫描未找超时回调记录.", batchNo);
            return;
        }

        for(Project project:timeOutRecordList){
            ProjectFlowRequestDTO requestDTO=new ProjectFlowRequestDTO();
            requestDTO.setProjectId(project.getProjectId());
            requestDTO.setSubStatus(ProjectSubStatusEnum.MISCARRY_BACK_OVERTIME);
            projectDubboService.flow(requestDTO);

            log.info("退回超时流标扫描,批次号={},标的ID={},已流标.", batchNo,project.getProjectId());
        }
    }


    public static List<RiskRuleRequest.Var> getVars() {
        List<RiskRuleRequest.Var> varList = new ArrayList<RiskRuleRequest.Var>();
        varList.add(new RiskRuleRequest.Var(RiskTypeEnum.I_HIT_OTHER_LOAN_INFO.getVarId()));
        varList.add(new RiskRuleRequest.Var(RiskTypeEnum.EMER_CONTACT_HIT_OTHER_LOAN_INFO.getVarId()));
        varList.add(new RiskRuleRequest.Var(RiskTypeEnum.COMPANY_PHONE_MATCH_INFO.getVarId()));
        varList.add(new RiskRuleRequest.Var(RiskTypeEnum.COMPANY_NAME_MATCH_INFO.getVarId()));
        varList.add(new RiskRuleRequest.Var(RiskTypeEnum.LOAN_GPS_LIVING_ADDRESS_INFO.getVarId()));
        varList.add(new RiskRuleRequest.Var(RiskTypeEnum.COMMON_AND_LIVING_ADDRESS_MATCH_INFO.getVarId()));
        varList.add(new RiskRuleRequest.Var(RiskTypeEnum.WIFI_EQUIP_MATCH_INFO.getVarId()));
        varList.add(new RiskRuleRequest.Var(RiskTypeEnum.GPS_POSITION_MATCH_INFO.getVarId()));
        varList.add(new RiskRuleRequest.Var(RiskTypeEnum.MORE_EUIP_LOAN_MATCH_INFO.getVarId()));
        varList.add(new RiskRuleRequest.Var(RiskTypeEnum.MORE_ACCOUNT_LOAN_MATCH_INFO.getVarId()));
        varList.add(new RiskRuleRequest.Var(RiskTypeEnum.HIGH_RISK_MSG_MATCH_INFO.getVarId()));
        varList.add(new RiskRuleRequest.Var(RiskTypeEnum.LOAN_APP_MATCH_INFO.getVarId()));

        return varList;
    }


    public static List<RiskRuleRequest.Var> getcardNiuVars() {
        List<RiskRuleRequest.Var> varList = new ArrayList<RiskRuleRequest.Var>();
        varList.add(new RiskRuleRequest.Var(RiskTypeEnum.I_HIT_OTHER_LOAN_INFO.getVarId()));
        varList.add(new RiskRuleRequest.Var(RiskTypeEnum.EMER_CONTACT_HIT_OTHER_LOAN_INFO.getVarId()));
        varList.add(new RiskRuleRequest.Var(RiskTypeEnum.COMPANY_PHONE_MATCH_INFO.getVarId()));
        varList.add(new RiskRuleRequest.Var(RiskTypeEnum.COMPANY_NAME_MATCH_INFO.getVarId()));
        varList.add(new RiskRuleRequest.Var(RiskTypeEnum.LOAN_GPS_LIVING_ADDRESS_INFO.getVarId()));
        varList.add(new RiskRuleRequest.Var(RiskTypeEnum.GPS_POSITION_MATCH_INFO.getVarId()));

        return varList;
    }

    @Override
    public void pushProject(PushProjectRequestDTO pushProjectRequestDTO) {
        Project project = projectCommonService.queryProjectBasicInfo(pushProjectRequestDTO.getProjectId());
        ISpeedLoanService service = iSpeedLoanFactory.adaptive(project);
        service.pushProject(pushProjectRequestDTO);
    }

    private EstablishProjectRequest transProject2EstablishProjectRequest(Project project){
        EstablishProjectRequest establishProjectRequest=new EstablishProjectRequest();
        establishProjectRequest.setRequestNo(lanMaoSequence.getRequestNo());
        establishProjectRequest.setProjectNo(String.valueOf(project.getProjectId()));
        establishProjectRequest.setProjectAmount(project.getContractAmount());
        establishProjectRequest.setProjectName(project.getTitle());
        establishProjectRequest.setProjectDescription(project.getTitle());
        establishProjectRequest.setProjectType(ProjectTypeEnum.STANDARDPOWDER);
        establishProjectRequest.setProjectPeriod(project.getDeadlineUnit().equals(DeadlineUnitEnum.BY_DAY.getDeadlineUnit())?project.getDeadline():project.getDeadline()*30);
        establishProjectRequest.setAnnnualInterestRate(project.getBorrowRate().divide(BigDecimal.valueOf(100)));
        establishProjectRequest.setRepaymentWay(loanLocalService.transRepaymentType2LM(RepaymentTypeEnum.enumOf(project.getRepaymentType())));

        return establishProjectRequest;
    }

    private boolean isCardNiu(Byte projectType){
        return projectType==null?false:(projectType.equals(com.niiwoo.civet.trade.enums.ProjectTypeEnum.CARD_NIU_LOAN.getValue())?true:false);
    }

    /**
     * 解密字符串
     *
     * @param str 密文
     */
    private String decryptString(String str) {
        if(!StringUtils.hasText(str)){
            return null;
        }
        return privacyMasks.decryptPrivacy(str);
    }

    /**
     * 加密字符串
     *
     * @param str 明文
     */
    private String ecryptString(String str) {
        if(!StringUtils.hasText(str)){
            return null;
        }
        return privacyMasks.encryptPrivacy(str);
    }

    @Override
    public LoanLimitResponseDTO listProjectInApproveOrNotSettled(String userId) {
        Assert.notNull(userId,"用户id不能为空");
        return speedLoanLimitLocalService.getProjectInApproveOrNotSettled(userId);
    }

    /**
     * 查询用户审核中或未结清标的数量,限定标的来源
     *
     * @param userId
     * @param fromSource
     * @return
     */
    @Override
    public LoanLimitResponseDTO listProjectInApproveOrNotSettledWithFromSource(String userId, ProjectFromSourceEnum fromSource) {
        Assert.notNull(userId,"用户id不能为空");
        Assert.notNull(fromSource,"标的来源不能为空");
        return speedLoanLimitLocalService.getProjectInApproveOrNotSettled(userId,fromSource);
    }

    @Override
    public SpeedLoanApplyCommonResponseDTO speedLoanPublishCityCheck(SpeedLoanPublishCheckRequestDTO requestDTO) {
        SpeedLoanApplyCommonResponseDTO applyCommonResponseDTO = new SpeedLoanApplyCommonResponseDTO();
        try {
            this.speedLoanPublishCheck_userCityCheck(requestDTO,applyCommonResponseDTO);
        } catch (Exception e) {
            applyCommonResponseDTO.setStatus(LoanLimitMessageEnum.LIMIT_10021.getLimitCode());
        }
        return applyCommonResponseDTO;
    }

	@Override
	public void confirmLoan(R360ConfirmLoanRequestDTO requestDTO) {
		log.info("融360订单确认借款, 请求参数：{}", JSONObject.toJSONString(requestDTO));

		// 基础数据较验
		Project project = null;
		if (StringUtils.isEmpty(requestDTO.getUserId())) {
			project = projectCommonService.queryProjectBasicInfo(requestDTO.getProjectId());
		} else {
			// 你我金额需求判断用户是否一致
			project = projectCommonService.selectByUserIdAndProjectId(requestDTO.getUserId(), requestDTO.getProjectId());
		}
		if (project == null) {
			log.error("融360订单确认借款, 标的不存在， ProjectId={}", requestDTO.getProjectId());
			throw new BizException("TRD20001");
		}

		// 重复操作，直接返回成功
		if (ProjectSubStatusEnum.COLLECTING_INIT.getSubStatus() == project.getSubStatus()) {
			log.info("融360订单确认借款, 标的已确认无需处理， ProjectId={}", requestDTO.getProjectId());
			return;
		}

		// 借款金额
		if (project.getContractAmount().compareTo(requestDTO.getLoanAmount()) != 0) {
			log.error("融360订单确认借款, 借款金额不匹配， ProjectId={}", requestDTO.getProjectId());
			throw new BizException("TRD60001");
		}

		// 借款期限
		if (project.getDeadline().compareTo(requestDTO.getDeadline()) != 0) {
			log.error("融360订单确认借款, 借款期限不匹配， ProjectId={}", requestDTO.getProjectId());
			throw new BizException("TRD60002");
		}

		// 订单状态非审核通过待确认或者审核通过时间为空，都认为标的状态不正确
		if (ProjectSubStatusEnum.CHECKING_FOR_CONFIRM.getSubStatus() != project.getSubStatus()
				|| null == project.getAuditedTime()) {
			log.error("融360订单确认借款, 标的状态不正确， ProjectId={}", requestDTO.getProjectId());
			throw new BizException("TRD60003");
		}

		// 是否在确认截至时间内
		if (new Date().after(getConfirmEndDate(project.getAuditedTime()))) {
			log.error("融360订单确认借款, 确认借款超时，ProjectId={}", requestDTO.getProjectId());
			throw new BizException("TRD60004");
		}

        projectLocalService.r360ConfirmLoan(requestDTO.getProjectId());
	}

    /**
     * 融360以及借款合同共用用户借款信息表
     * @param projectId
     * @return
     */
    @Override
    public ConfirmResponseDTO userLoanInfo(Long projectId){
        return loanConfirmService.userLoanInfo(projectId);
    }

    /**
     * 砍头息借款确认接口
     * @param confirmLoanRequestDTO
     * @return
     */
    @Override
    public ConfirmResponseDTO calculateConfirm(ConfirmLoanRequestDTO confirmLoanRequestDTO){
        return loanConfirmService.loanConfirm(confirmLoanRequestDTO);
    }

    /**
     * 借款确认页面需要的还款详情
     * @param loanConfirmRequestDTO
     * @return
     */
    @Override
    public List<RepaymentPlanDTO> repaymentPlan(ConfirmLoanRepaymentPlanRequestDTO loanConfirmRequestDTO) {
        return loanConfirmService.repaymentPlan(loanConfirmRequestDTO);
    }

    /**
     * 还款服务委托书生成
     * @param userId
     * @param projectId
     * @return
     */
    public FadadaWithholdContractSignResponseDTO fadadaWithholdContractSign(String userId, Long projectId){
        FadadaWithholdContractSignResponseDTO resultDTO = new FadadaWithholdContractSignResponseDTO();
        resultDTO.setSignResult(FadadaWithholdContractSignResponseDTO.FAIL);

        try {
            if(this.isContractSign(userId)){
                log.info("fadadaWithholdContractSign还款服务委托书合同已经生成,userId={}",userId);
                resultDTO.setSignResult(FadadaWithholdContractSignResponseDTO.SUCCESS);
                return resultDTO;
            }

            //先修改签约状态为已经签约
            updateDataStatus(userId,UserDataStatusEnum.WithholdContractSignStatus.ALREADY_SIGN);
            loanLocalService.fadadaWithholdContractSign(userId,projectId);

            resultDTO.setSignResult(FadadaWithholdContractSignResponseDTO.SUCCESS);
        } catch (Exception e) {
            log.error("fadadaWithholdContractSign还款服务委托书合同生成异常,userId={}",userId,e);
        }
        return resultDTO;
    }

    private boolean isContractSign(String userId){
        log.info("isContractSign start userId={}",userId);
        DataStatusResponseDTO withholdContractSignStatus = userStatusDubboService.getByUserIdAndType(userId, UserDataTypeEnum.WITHHOLD_CONTRACT_SIGN);
        log.info("isContractSign.userId={},withholdContractSignStatus={}",userId,JSON.toJSONString(withholdContractSignStatus));
        if(withholdContractSignStatus!=null
                && UserDataStatusEnum.WithholdContractSignStatus.ALREADY_SIGN.getStatus().intValue()==withholdContractSignStatus.getDataStatus().intValue()){
            return true;
        }
        return false;
    }

    private void updateDataStatus(String userId,UserDataStatusEnum.WithholdContractSignStatus dataStatusEnum){
        Date now = new Date();
        DataStatusRequestDTO dataStatusDTO = new DataStatusRequestDTO();
        dataStatusDTO.setUserId(userId);
        dataStatusDTO.setDataType(UserDataTypeEnum.WITHHOLD_CONTRACT_SIGN.getValue());
        dataStatusDTO.setDataStatus(dataStatusEnum.getStatus());
        dataStatusDTO.setAuthTime(now);
        dataStatusDTO.setCreateTime(now);
        userStatusDubboService.saveDataStatusByUserIdAndType(dataStatusDTO);
    }

    /**
     * 获取用户借款成功次数
     * @param userId
     * @return
     */
    public int getUserLoanSuccessTimes(String userId){
        //获取借款成功次数
        return projectCommonService.statisticsProjectCountByStatus(userId, Arrays.asList(ProjectStatusEnum.REPAYING.getStatus(), ProjectStatusEnum.FINISH.getStatus()), null);
    }

    /**
     * 退回标的
     *
     * @param projectId
     * @param projectSubStatusEnum
     */
    @Override
    public void returnProject(Long projectId, ProjectSubStatusEnum projectSubStatusEnum) {
        loanLocalService.returnProject(projectId, projectSubStatusEnum);
    }

    /**
     * 获取融360确认截至时间
     *
     * @param
     * @return
     */
    private Date getConfirmEndDate(Date auditedTime) {
		String sureTimeConfigValue = tradeConfigService.selectTradeConfigByEmun(TradeConfigEnum.LOAN_RONG360SURETIME);

		Integer sureTime = 24; // 默认24小时
		if (!StringUtils.isEmpty(sureTimeConfigValue)) {
			sureTime = Integer.valueOf(sureTimeConfigValue);
		}
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(auditedTime);
        calendar.add(Calendar.HOUR_OF_DAY, sureTime);
        return calendar.getTime();
    }

    @Override
    public List<FastBorrowAccreditCityResponseDTO> fastBorrowAccreditCity() {
        //获取开放城市
        List<FastBorrowAccreditCityResponseDTO> responseDTOList = new ArrayList<>();
        FastBorrowAccreditCityResponseDTO other = new FastBorrowAccreditCityResponseDTO();
        FastBorrowAccreditCityResponseDTO responseDTO = null;
        String speedBorrowCityCode = tradeConfigService.selectTradeConfigByEmun(TradeConfigEnum.SPEEDBORROWCITYCODE);
        log.info("开通城市查询结果：speedBorrowCityCode=" + speedBorrowCityCode);
        String[] areaCodeArray = speedBorrowCityCode.split(",");
        List<String> areaCodeList = new ArrayList<>();
        Collections.addAll(areaCodeList, areaCodeArray);
        List<UserAreaDictionaryDTO> areaCodeListDTO = userDataDubboService.selectFastBorrowAccreditCity(areaCodeList);
        FastBorrowAccreditCityResponseDTO.AccreditCity accreditCity = null;
        for( UserAreaDictionaryDTO dictionaryDTO : areaCodeListDTO){
            responseDTO = new FastBorrowAccreditCityResponseDTO();
            if(dictionaryDTO.getParentCode().equals("0") && !dictionaryDTO.getAreaName().equals("其他")){
                accreditCity = new FastBorrowAccreditCityResponseDTO.AccreditCity();
                accreditCity.setCity(dictionaryDTO.getAreaName());
                responseDTO.setProvinceName(dictionaryDTO.getAreaName());
                responseDTO.getCitys().add(accreditCity);
                responseDTOList.add(responseDTO);
            }else if(dictionaryDTO.getParentCode().equals("0") && dictionaryDTO.getAreaName().equals("其他")) {
                accreditCity = new FastBorrowAccreditCityResponseDTO.AccreditCity();
                accreditCity.setCity(dictionaryDTO.getAreaName());
                other.setProvinceName(dictionaryDTO.getAreaName());
                other.getCitys().add(accreditCity);
            }
        }
        List<String> parentCodeList = new ArrayList<>();
        areaCodeListDTO.forEach(d -> {
            if(!parentCodeList.contains(d.getParentCode()) && !d.getParentCode().equals("0")){
                parentCodeList.add(d.getParentCode());
            }
        });
        List<UserAreaDictionaryDTO> parentCodeListDTO = userDataDubboService.selectFastBorrowAccreditCity(parentCodeList);
        for(UserAreaDictionaryDTO dto : parentCodeListDTO) {
            responseDTO = new FastBorrowAccreditCityResponseDTO();
            responseDTO.setProvinceName(dto.getAreaName());
            for (UserAreaDictionaryDTO dictionary : areaCodeListDTO) {
                if (dictionary.getParentCode().equals(dto.getAreaCode())) {
                    accreditCity = new FastBorrowAccreditCityResponseDTO.AccreditCity();
                    accreditCity.setCity(dictionary.getAreaName());
                    responseDTO.getCitys().add(accreditCity);
                }
            }
            responseDTOList.add(responseDTO);
        }
        Collections.sort(responseDTOList);
        responseDTOList.add(other);
        return responseDTOList;
    }


    @Override
    public void addAuditWorkTask(Long projectId) {
        ProjectDTO project = projectDubboService.selectProjectById(projectId);
        Project p = new Project();
        p.setProjectId(project.getProjectId());
        p.setBorrowUserId(project.getBorrowUserId());
        p.setBorrowUserName(project.getBorrowUserName());
        p.setBorrowMobile(project.getBorrowMobile());
        p.setChannelCode(project.getChannelCode());
        p.setSubmitTime(project.getSubmitTime());
        loanLocalService.addAuditWorkTask(p);
    }
}
